diff --git a/eng/testing/tests.browser.targets b/eng/testing/tests.browser.targets
index 16a80cea267019..7c0a0f86f0d6e8 100644
--- a/eng/testing/tests.browser.targets
+++ b/eng/testing/tests.browser.targets
@@ -105,6 +105,8 @@
<_AppArgs Condition="'$(IsFunctionalTest)' != 'true' and '$(WasmMainAssemblyFileName)' != ''">--run $(WasmMainAssemblyFileName)
<_AppArgs Condition="'$(IsFunctionalTest)' == 'true'">--run $(AssemblyName).dll
+ <_XUnitBackgroundExec Condition="'$(_XUnitBackgroundExec)' == '' and '$(MonoWasmBuildVariant)' == 'multithread'">true
+ $(WasmTestAppArgs) -backgroundExec
<_AppArgs Condition="'$(WasmTestAppArgs)' != ''">$(_AppArgs) $(WasmTestAppArgs)
$(WasmXHarnessMonoArgs) --setenv=XHARNESS_LOG_TEST_START=1
diff --git a/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs b/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs
index 0134102a042045..9fb129d825d742 100644
--- a/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs
+++ b/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs
@@ -23,6 +23,7 @@ public static async Task Main(string[] args)
var includedNamespaces = new List();
var includedClasses = new List();
var includedMethods = new List();
+ var backgroundExec = false;
for (int i = 1; i < args.Length; i++)
{
@@ -49,6 +50,9 @@ public static async Task Main(string[] args)
includedMethods.Add (args[i + 1]);
i++;
break;
+ case "-backgroundExec":
+ backgroundExec = true;
+ break;
default:
throw new ArgumentException($"Invalid argument '{option}'.");
}
@@ -68,7 +72,10 @@ public static async Task Main(string[] args)
{
await Task.Yield();
}
-
+ if (backgroundExec)
+ {
+ return await Task.Run(() => runner.Run());
+ }
return await runner.Run();
}
}
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System.Runtime.InteropServices.JavaScript.Legacy.Tests.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System.Runtime.InteropServices.JavaScript.Legacy.Tests.csproj
index 41aaf18747b027..80bdbabd3b1d8b 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System.Runtime.InteropServices.JavaScript.Legacy.Tests.csproj
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System.Runtime.InteropServices.JavaScript.Legacy.Tests.csproj
@@ -8,6 +8,8 @@
true
$(DefineConstants);DISABLE_LEGACY_JS_INTEROP
WasmTestOnBrowser
+
+ <_XUnitBackgroundExec>false
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj
index 13a08edcbf23b8..c4d5494f93718f 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj
@@ -10,6 +10,8 @@
$(DefineConstants);DISABLE_LEGACY_JS_INTEROP
true
+
+ <_XUnitBackgroundExec>false
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs
index 18b53e61d920cf..26227d1f00549c 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs
@@ -1004,6 +1004,9 @@ public static async Task InitializeAsync()
await Setup();
// Log("JavaScriptTestHelper.mjs imported");
}
+
+ // this gives browser chance to serve UI thread event loop before every test
+ await Task.Yield();
}
public static Task DisposeAsync()
diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj
index 05605d51f70069..1fb4d6f1a6c123 100644
--- a/src/libraries/tests.proj
+++ b/src/libraries/tests.proj
@@ -390,6 +390,9 @@
+
+
+
@@ -592,6 +595,7 @@
-->
+
diff --git a/src/mono/wasm/runtime/cwraps.ts b/src/mono/wasm/runtime/cwraps.ts
index 4026be432c3004..847c0e5c0fd627 100644
--- a/src/mono/wasm/runtime/cwraps.ts
+++ b/src/mono/wasm/runtime/cwraps.ts
@@ -41,6 +41,7 @@ const threading_cwraps: SigLine[] = MonoWasmThreads ? [
[true, "mono_wasm_diagnostic_server_thread_attach_to_runtime", "void", []],
[true, "mono_wasm_diagnostic_server_post_resume_runtime", "void", []],
[true, "mono_wasm_diagnostic_server_create_stream", "number", []],
+ [false, "mono_wasm_init_finalizer_thread", null, []],
] : [];
// when the method is assigned/cached at usage, instead of being invoked directly from cwraps, it can't be marked lazy, because it would be re-bound on each call
@@ -181,6 +182,7 @@ export interface t_ThreadingCwraps {
mono_wasm_diagnostic_server_thread_attach_to_runtime(): void;
mono_wasm_diagnostic_server_post_resume_runtime(): void;
mono_wasm_diagnostic_server_create_stream(): VoidPtr;
+ mono_wasm_init_finalizer_thread(): void;
}
export interface t_ProfilerCwraps {
diff --git a/src/mono/wasm/runtime/driver.c b/src/mono/wasm/runtime/driver.c
index d7e729e669b62a..3ebaa52edfdf18 100644
--- a/src/mono/wasm/runtime/driver.c
+++ b/src/mono/wasm/runtime/driver.c
@@ -58,7 +58,6 @@ char *mono_method_get_full_name (MonoMethod *method);
extern void mono_register_timezones_bundle (void);
#endif /* INVARIANT_TIMEZONE */
extern void mono_wasm_set_entrypoint_breakpoint (const char* assembly_name, int method_token);
-static void mono_wasm_init_finalizer_thread (void);
extern void mono_bundled_resources_add_assembly_resource (const char *id, const char *name, const uint8_t *data, uint32_t size, void (*free_func)(void *, void*), void *free_data);
extern void mono_bundled_resources_add_assembly_symbol_resource (const char *id, const uint8_t *data, uint32_t size, void (*free_func)(void *, void *), void *free_data);
@@ -542,9 +541,6 @@ mono_wasm_load_runtime (const char *unused, int debug_level)
mono_thread_set_main (mono_thread_current ());
- // TODO: we can probably delay starting the finalizer thread even longer - maybe from JS
- // once we're done with loading and are about to begin running some managed code.
- mono_wasm_init_finalizer_thread ();
}
EMSCRIPTEN_KEEPALIVE MonoAssembly*
@@ -1296,12 +1292,11 @@ mono_wasm_profiler_init_browser (const char *desc)
#endif
-static void
+EMSCRIPTEN_KEEPALIVE void
mono_wasm_init_finalizer_thread (void)
{
- // At this time we don't use a dedicated thread for finalization even if threading is enabled.
- // Finalizers periodically run on the main thread
-#if 0
+ // in the single threaded build, finalizers periodically run on the main thread instead.
+#ifndef DISABLE_THREADS
mono_gc_init_finalizer_thread ();
#endif
}
diff --git a/src/mono/wasm/runtime/exports-binding.ts b/src/mono/wasm/runtime/exports-binding.ts
index e4f2b1fd0ea143..50f3c14c0a313c 100644
--- a/src/mono/wasm/runtime/exports-binding.ts
+++ b/src/mono/wasm/runtime/exports-binding.ts
@@ -19,7 +19,7 @@ import { mono_wasm_asm_loaded } from "./startup";
import { mono_wasm_diagnostic_server_on_server_thread_created } from "./diagnostics/server_pthread";
import { mono_wasm_diagnostic_server_on_runtime_server_init, mono_wasm_event_pipe_early_startup_callback } from "./diagnostics";
import { mono_wasm_diagnostic_server_stream_signal_work_available } from "./diagnostics/server_pthread/stream-queue";
-import { mono_log_debug, mono_log_warn, mono_wasm_trace_logger } from "./logging";
+import { mono_log_warn, mono_wasm_trace_logger } from "./logging";
import { mono_wasm_profiler_leave, mono_wasm_profiler_enter } from "./profiler";
import { mono_wasm_change_case, mono_wasm_change_case_invariant } from "./hybrid-globalization/change-case";
import { mono_wasm_compare_string, mono_wasm_ends_with, mono_wasm_starts_with, mono_wasm_index_of } from "./hybrid-globalization/collations";
@@ -163,8 +163,6 @@ export function replace_linker_placeholders(imports: WebAssembly.Imports) {
const stubFn = env[shortName];
if (typeof stubFn !== "function") throw new Error(`Expected ${shortName} to be a function`);
env[shortName] = realFn;
- mono_log_debug(`Replaced WASM import ${shortName} stub ${stubFn.name} with ${realFn.name || "minified implementation"}`);
}
}
-
}
diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts
index f59dbabbe57999..556dd158c9700c 100644
--- a/src/mono/wasm/runtime/startup.ts
+++ b/src/mono/wasm/runtime/startup.ts
@@ -6,7 +6,7 @@ import WasmEnableLegacyJsInterop from "consts:wasmEnableLegacyJsInterop";
import { DotnetModuleInternal, CharPtrNull } from "./types/internal";
import { linkerDisableLegacyJsInterop, ENVIRONMENT_IS_PTHREAD, ENVIRONMENT_IS_NODE, exportedRuntimeAPI, INTERNAL, loaderHelpers, Module, runtimeHelpers, createPromiseController, mono_assert, linkerWasmEnableSIMD, linkerWasmEnableEH, ENVIRONMENT_IS_WORKER } from "./globals";
-import cwraps, { init_c_exports } from "./cwraps";
+import cwraps, { init_c_exports, threads_c_functions as tcwraps } from "./cwraps";
import { mono_wasm_raise_debug_event, mono_wasm_runtime_ready } from "./debug";
import { toBase64StringImpl } from "./base64";
import { mono_wasm_init_aot_profiler, mono_wasm_init_browser_profiler } from "./profiler";
@@ -334,6 +334,10 @@ async function postRunAsync(userpostRun: (() => void)[]) {
Module["FS_createPath"]("/", "usr", true, true);
Module["FS_createPath"]("/", "usr/share", true, true);
+ if (MonoWasmThreads) {
+ tcwraps.mono_wasm_init_finalizer_thread();
+ }
+
// all user Module.postRun callbacks
userpostRun.map(fn => fn());
endMeasure(mark, MeasuredBlock.postRun);
@@ -561,7 +565,7 @@ async function mono_wasm_before_memory_snapshot() {
endMeasure(mark, MeasuredBlock.memorySnapshot);
}
-async function maybeSaveInterpPgoTable () {
+async function maybeSaveInterpPgoTable() {
// If the application exited abnormally, don't save the table. It probably doesn't contain useful data,
// and saving would overwrite any existing table from a previous successful run.
// We treat exiting with a code of 0 as equivalent to if the app is still running - it's perfectly fine
diff --git a/src/mono/wasm/test-main.js b/src/mono/wasm/test-main.js
index 4467dc20ce204e..a8cd689ae0c32f 100644
--- a/src/mono/wasm/test-main.js
+++ b/src/mono/wasm/test-main.js
@@ -350,11 +350,11 @@ async function run() {
if (ENVIRONMENT_IS_WEB && runArgs.memorySnapshot) {
if (globalThis.isSecureContext) {
- const dryOk = await dry_run(runArgs);
- if (!dryOk) {
- mono_exit(1, "Failed during dry run");
- return;
- }
+ const dryOk = await dry_run(runArgs);
+ if (!dryOk) {
+ mono_exit(1, "Failed during dry run");
+ return;
+ }
} else {
console.log("Skipping dry run as the context is not secure and the snapshot would be not trusted.");
}