diff --git a/src/mono/browser/browser.proj b/src/mono/browser/browser.proj
index 09123abe3d19b5..f7429193002a60 100644
--- a/src/mono/browser/browser.proj
+++ b/src/mono/browser/browser.proj
@@ -207,6 +207,7 @@
+
diff --git a/src/mono/browser/runtime/cwraps.ts b/src/mono/browser/runtime/cwraps.ts
index 3ce3302e3b3514..ed64d5c541d4b8 100644
--- a/src/mono/browser/runtime/cwraps.ts
+++ b/src/mono/browser/runtime/cwraps.ts
@@ -63,7 +63,6 @@ const fn_signatures: SigLine[] = [
[true, "mono_wasm_intern_string_ref", "void", ["number"]],
[false, "mono_wasm_exit", "void", ["number"]],
- [false, "mono_wasm_abort", "void", []],
[true, "mono_wasm_getenv", "number", ["string"]],
[true, "mono_wasm_set_main_args", "void", ["number", "number"]],
// These two need to be lazy because they may be missing
@@ -192,7 +191,6 @@ export interface t_Cwraps {
mono_wasm_intern_string_ref(strRef: MonoStringRef): void;
mono_wasm_exit(exit_code: number): void;
- mono_wasm_abort(): void;
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;
diff --git a/src/mono/browser/runtime/driver.c b/src/mono/browser/runtime/driver.c
index bca878eb1820de..1b3663e4f3f3ca 100644
--- a/src/mono/browser/runtime/driver.c
+++ b/src/mono/browser/runtime/driver.c
@@ -347,12 +347,6 @@ mono_wasm_exit (int exit_code)
emscripten_force_exit (exit_code);
}
-EMSCRIPTEN_KEEPALIVE int
-mono_wasm_abort ()
-{
- abort ();
-}
-
EMSCRIPTEN_KEEPALIVE void
mono_wasm_set_main_args (int argc, char* argv[])
{
diff --git a/src/mono/browser/runtime/invoke-js.ts b/src/mono/browser/runtime/invoke-js.ts
index 07c02c719d4851..fa6949f2d636aa 100644
--- a/src/mono/browser/runtime/invoke-js.ts
+++ b/src/mono/browser/runtime/invoke-js.ts
@@ -60,7 +60,7 @@ export function mono_wasm_invoke_jsimport_MT (signature: JSFunctionSignature, ar
}
return;
} catch (ex2: any) {
- runtimeHelpers.nativeExit(ex2);
+ runtimeHelpers.nativeAbort(ex2);
return;
}
}
diff --git a/src/mono/browser/runtime/loader/exit.ts b/src/mono/browser/runtime/loader/exit.ts
index 2264cd086fbd7a..cdea5d5997265b 100644
--- a/src/mono/browser/runtime/loader/exit.ts
+++ b/src/mono/browser/runtime/loader/exit.ts
@@ -39,36 +39,40 @@ export function uninstallUnhandledErrorHandler () {
}
}
+let originalOnAbort: ((reason: any, extraJson?:string)=>void)|undefined;
+let originalOnExit: ((code: number)=>void)|undefined;
+
export function registerEmscriptenExitHandlers () {
- if (!emscriptenModule.onAbort) {
- emscriptenModule.onAbort = onAbort;
- }
- if (!emscriptenModule.onExit) {
- emscriptenModule.onExit = onExit;
- }
+ originalOnAbort = emscriptenModule.onAbort;
+ originalOnExit = emscriptenModule.onExit;
+ emscriptenModule.onAbort = onAbort;
+ emscriptenModule.onExit = onExit;
}
function unregisterEmscriptenExitHandlers () {
if (emscriptenModule.onAbort == onAbort) {
- emscriptenModule.onAbort = undefined;
+ emscriptenModule.onAbort = originalOnAbort;
}
if (emscriptenModule.onExit == onExit) {
- emscriptenModule.onExit = undefined;
+ emscriptenModule.onExit = originalOnExit;
}
}
function onExit (code: number) {
+ if (originalOnExit) {
+ originalOnExit(code);
+ }
mono_exit(code, loaderHelpers.exitReason);
}
function onAbort (reason: any) {
- mono_exit(1, loaderHelpers.exitReason || reason);
+ if (originalOnAbort) {
+ originalOnAbort(reason || loaderHelpers.exitReason);
+ }
+ mono_exit(1, reason || loaderHelpers.exitReason);
}
// this will also call mono_wasm_exit if available, which will call exitJS -> _proc_exit -> terminateAllThreads
export function mono_exit (exit_code: number, reason?: any): void {
- unregisterEmscriptenExitHandlers();
- uninstallUnhandledErrorHandler();
-
// unify shape of the reason object
const is_object = reason && typeof reason === "object";
exit_code = (is_object && typeof reason.status === "number")
@@ -82,7 +86,7 @@ export function mono_exit (exit_code: number, reason?: any): void {
reason = is_object
? reason
: (runtimeHelpers.ExitStatus
- ? new runtimeHelpers.ExitStatus(exit_code)
+ ? createExitStatus(exit_code, message)
: new Error("Exit with code " + exit_code + " " + message));
reason.status = exit_code;
if (!reason.message) {
@@ -90,15 +94,19 @@ export function mono_exit (exit_code: number, reason?: any): void {
}
// force stack property to be generated before we shut down managed code, or create current stack if it doesn't exist
- if (!reason.stack) {
- reason.stack = new Error().stack || "";
- }
+ const stack = "" + (reason.stack || (new Error().stack));
+ Object.defineProperty(reason, "stack", {
+ get: () => stack
+ });
// don't report this error twice
+ const alreadySilent = !!reason.silent;
reason.silent = true;
if (!is_exited()) {
try {
+ unregisterEmscriptenExitHandlers();
+ uninstallUnhandledErrorHandler();
if (!runtimeHelpers.runtimeReady) {
mono_log_debug("abort_startup, reason: " + reason);
abort_promises(reason);
@@ -119,19 +127,25 @@ export function mono_exit (exit_code: number, reason?: any): void {
}
try {
- logOnExit(exit_code, reason);
- appendElementOnExit(exit_code);
+ if (!alreadySilent) {
+ logOnExit(exit_code, reason);
+ appendElementOnExit(exit_code);
+ }
} catch (err) {
mono_log_warn("mono_exit failed", err);
// don't propagate any failures
}
loaderHelpers.exitCode = exit_code;
- loaderHelpers.exitReason = reason.message;
+ if (!loaderHelpers.exitReason) {
+ loaderHelpers.exitReason = reason;
+ }
if (!ENVIRONMENT_IS_WORKER && runtimeHelpers.runtimeReady) {
emscriptenModule.runtimeKeepalivePop();
}
+ } else {
+ mono_log_debug("mono_exit called after exit");
}
if (loaderHelpers.config && loaderHelpers.config.asyncFlushOnExit && exit_code === 0) {
@@ -154,13 +168,11 @@ export function mono_exit (exit_code: number, reason?: any): void {
function set_exit_code_and_quit_now (exit_code: number, reason?: any): void {
if (WasmEnableThreads && ENVIRONMENT_IS_WORKER && runtimeHelpers.runtimeReady && runtimeHelpers.nativeAbort) {
// note that the reason is not passed to UI thread
- runtimeHelpers.runtimeReady = false;
runtimeHelpers.nativeAbort(reason);
throw reason;
}
if (runtimeHelpers.runtimeReady && runtimeHelpers.nativeExit) {
- runtimeHelpers.runtimeReady = false;
try {
runtimeHelpers.nativeExit(exit_code);
} catch (error: any) {
@@ -205,7 +217,6 @@ async function flush_node_streams () {
}
function abort_promises (reason: any) {
- loaderHelpers.exitReason = reason;
loaderHelpers.allDownloadsQueued.promise_control.reject(reason);
loaderHelpers.afterConfigLoaded.promise_control.reject(reason);
loaderHelpers.wasmCompilePromise.promise_control.reject(reason);
@@ -256,7 +267,7 @@ function logOnExit (exit_code: number, reason: any) {
}
}
}
- if (loaderHelpers.config) {
+ if (!ENVIRONMENT_IS_WORKER && loaderHelpers.config) {
if (loaderHelpers.config.logExitCode) {
if (loaderHelpers.config.forwardConsoleLogsToWS) {
teardown_proxy_console("WASM EXIT " + exit_code);
@@ -294,3 +305,10 @@ function fatal_handler (event: any, reason: any, type: string) {
// no not re-throw from the fatal handler
}
}
+
+function createExitStatus (status:number, message:string) {
+ const ex = new runtimeHelpers.ExitStatus(status);
+ ex.message = message;
+ ex.toString = () => message;
+ return ex;
+}
diff --git a/src/mono/browser/runtime/loader/logging.ts b/src/mono/browser/runtime/loader/logging.ts
index 668d7e667b6d3f..ce98616e08d0ee 100644
--- a/src/mono/browser/runtime/loader/logging.ts
+++ b/src/mono/browser/runtime/loader/logging.ts
@@ -42,9 +42,6 @@ export function mono_log_error (msg: string, ...data: any) {
if (data[0].silent) {
return;
}
- if (data[0].toString) {
- console.error(prefix + msg, data[0].toString());
- }
if (data[0].toString) {
console.error(prefix + msg, data[0].toString());
return;
@@ -118,12 +115,13 @@ export function setup_proxy_console (id: string, console: Console, origin: strin
}
export function teardown_proxy_console (message?: string) {
+ let counter = 30;
const stop_when_ws_buffer_empty = () => {
if (!consoleWebSocket) {
if (message && originalConsoleMethods) {
originalConsoleMethods.log(message);
}
- } else if (consoleWebSocket.bufferedAmount == 0) {
+ } else if (consoleWebSocket.bufferedAmount == 0 || counter == 0) {
if (message) {
// tell xharness WasmTestMessagesProcessor we are done.
// note this sends last few bytes into the same WS
@@ -136,6 +134,7 @@ export function teardown_proxy_console (message?: string) {
consoleWebSocket.close(1000, message);
(consoleWebSocket as any) = undefined;
} else {
+ counter--;
globalThis.setTimeout(stop_when_ws_buffer_empty, 100);
}
};
diff --git a/src/mono/browser/runtime/logging.ts b/src/mono/browser/runtime/logging.ts
index 1d91a870d52fb4..3558f24dc6c018 100644
--- a/src/mono/browser/runtime/logging.ts
+++ b/src/mono/browser/runtime/logging.ts
@@ -1,8 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-/* eslint-disable no-console */
-import { INTERNAL, runtimeHelpers, mono_assert } from "./globals";
+import WasmEnableThreads from "consts:wasmEnableThreads";
+
+import { threads_c_functions as tcwraps } from "./cwraps";
+import { INTERNAL, runtimeHelpers, mono_assert, loaderHelpers, ENVIRONMENT_IS_WORKER, Module } from "./globals";
import { utf8ToString } from "./strings";
import { CharPtr, VoidPtr } from "./types/emscripten";
@@ -12,6 +14,7 @@ export function set_thread_prefix (threadPrefix: string) {
prefix = `[${threadPrefix}] MONO_WASM: `;
}
+/* eslint-disable no-console */
export function mono_log_debug (msg: string, ...data: any) {
if (runtimeHelpers.diagnosticTracing) {
console.debug(prefix + msg, ...data);
@@ -27,9 +30,15 @@ export function mono_log_warn (msg: string, ...data: any) {
}
export function mono_log_error (msg: string, ...data: any) {
- if (data && data.length > 0 && data[0] && typeof data[0] === "object" && data[0].silent) {
+ if (data && data.length > 0 && data[0] && typeof data[0] === "object") {
// don't log silent errors
- return;
+ if (data[0].silent) {
+ return;
+ }
+ if (data[0].toString) {
+ console.error(prefix + msg, data[0].toString());
+ return;
+ }
}
console.error(prefix + msg, ...data);
}
@@ -123,7 +132,27 @@ export function mono_wasm_trace_logger (log_domain_ptr: CharPtr, log_level_ptr:
switch (log_level) {
case "critical":
case "error":
- console.error(mono_wasm_stringify_as_error_with_stack(message));
+ {
+ const messageWithStack = message + "\n" + (new Error().stack);
+ if (!loaderHelpers.exitReason) {
+ loaderHelpers.exitReason = messageWithStack;
+ }
+ console.error(mono_wasm_stringify_as_error_with_stack(messageWithStack));
+ if (WasmEnableThreads) {
+ try {
+ tcwraps.mono_wasm_print_thread_dump();
+ } catch (e) {
+ console.error("Failed to print thread dump", e);
+ }
+ }
+ if (WasmEnableThreads && ENVIRONMENT_IS_WORKER) {
+ setTimeout(() => {
+ mono_log_error("forcing abort 3000ms after last error log message", messageWithStack);
+ // _emscripten_force_exit is proxied to UI thread and should also arrive in spin wait loop
+ Module._emscripten_force_exit(1);
+ }, 3000);
+ }
+ }
break;
case "warning":
console.warn(message);
diff --git a/src/mono/browser/runtime/managed-exports.ts b/src/mono/browser/runtime/managed-exports.ts
index 065136faaba476..79bbcbf2af964c 100644
--- a/src/mono/browser/runtime/managed-exports.ts
+++ b/src/mono/browser/runtime/managed-exports.ts
@@ -14,7 +14,7 @@ import { assert_c_interop, assert_js_interop } from "./invoke-js";
import { monoThreadInfo, mono_wasm_main_thread_ptr } from "./pthreads";
import { _zero_region, copyBytes } from "./memory";
import { stringToUTF8Ptr } from "./strings";
-import { mono_log_debug } from "./logging";
+import { mono_log_error } from "./logging";
const managedExports: ManagedExports = {} as any;
@@ -269,7 +269,7 @@ export function install_main_synchronization_context (jsThreadBlockingMode: JSTh
}
return get_arg_gc_handle(res) as any;
} catch (e) {
- mono_log_debug("install_main_synchronization_context failed", e);
+ mono_log_error("install_main_synchronization_context failed", e);
throw e;
}
}
diff --git a/src/mono/browser/runtime/pthreads/index.ts b/src/mono/browser/runtime/pthreads/index.ts
index 25f981d3f3b697..87ed1b006184db 100644
--- a/src/mono/browser/runtime/pthreads/index.ts
+++ b/src/mono/browser/runtime/pthreads/index.ts
@@ -5,13 +5,14 @@ import { mono_log_warn } from "../logging";
import { utf16ToString } from "../strings";
export {
- mono_wasm_main_thread_ptr, mono_wasm_install_js_worker_interop, mono_wasm_uninstall_js_worker_interop,
+ mono_wasm_main_thread_ptr,
mono_wasm_pthread_ptr, update_thread_info, isMonoThreadMessage, monoThreadInfo,
} from "./shared";
+export { mono_wasm_install_js_worker_interop, mono_wasm_uninstall_js_worker_interop } from "./worker-interop";
export {
- mono_wasm_dump_threads, cancelThreads,
+ mono_wasm_dump_threads, postCancelThreads,
populateEmscriptenPool, mono_wasm_init_threads,
- waitForThread, replaceEmscriptenPThreadUI
+ waitForThread, replaceEmscriptenPThreadUI, terminateAllThreads,
} from "./ui-thread";
export {
mono_wasm_pthread_on_pthread_attached, mono_wasm_pthread_on_pthread_unregistered,
diff --git a/src/mono/browser/runtime/pthreads/shared.ts b/src/mono/browser/runtime/pthreads/shared.ts
index f72804fbcf873c..c266c2dfec66e4 100644
--- a/src/mono/browser/runtime/pthreads/shared.ts
+++ b/src/mono/browser/runtime/pthreads/shared.ts
@@ -6,11 +6,9 @@ import BuildConfiguration from "consts:configuration";
import type { GCHandle, MonoThreadMessage, PThreadInfo, PThreadPtr } from "../types/internal";
-import { ENVIRONMENT_IS_PTHREAD, Module, loaderHelpers, mono_assert, runtimeHelpers } from "../globals";
+import { Module, loaderHelpers, runtimeHelpers } from "../globals";
import { set_thread_prefix } from "../logging";
-import { bindings_init } from "../startup";
-import { forceDisposeProxies } from "../gc-handles";
-import { monoMessageSymbol, GCHandleNull, PThreadPtrNull, WorkerToMainMessageType } from "../types/internal";
+import { monoMessageSymbol, PThreadPtrNull, WorkerToMainMessageType } from "../types/internal";
import { threads_c_functions as tcwraps } from "../cwraps";
import { forceThreadMemoryViewRefresh } from "../memory";
@@ -34,39 +32,6 @@ export function isMonoThreadMessage (x: unknown): x is MonoThreadMessage {
return typeof (xmsg.type) === "string" && typeof (xmsg.cmd) === "string";
}
-export function mono_wasm_install_js_worker_interop (context_gc_handle: GCHandle): void {
- if (!WasmEnableThreads) return;
- bindings_init();
- mono_assert(!runtimeHelpers.proxyGCHandle, "JS interop should not be already installed on this worker.");
- runtimeHelpers.proxyGCHandle = context_gc_handle;
- if (ENVIRONMENT_IS_PTHREAD) {
- runtimeHelpers.managedThreadTID = runtimeHelpers.currentThreadTID;
- runtimeHelpers.isManagedRunningOnCurrentThread = true;
- }
- Module.runtimeKeepalivePush();
- monoThreadInfo.isDirtyBecauseOfInterop = true;
- update_thread_info();
- if (ENVIRONMENT_IS_PTHREAD) {
- postMessageToMain({
- monoCmd: WorkerToMainMessageType.enabledInterop,
- info: monoThreadInfo,
- });
- }
-}
-
-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.proxyGCHandle, "JSSynchronizationContext is not installed on this worker.");
-
- forceDisposeProxies(true, runtimeHelpers.diagnosticTracing);
- Module.runtimeKeepalivePop();
-
- 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 {
if (!WasmEnableThreads) return;
diff --git a/src/mono/browser/runtime/pthreads/ui-thread.ts b/src/mono/browser/runtime/pthreads/ui-thread.ts
index b5f4f72875ec12..0dc2b80c57e0f8 100644
--- a/src/mono/browser/runtime/pthreads/ui-thread.ts
+++ b/src/mono/browser/runtime/pthreads/ui-thread.ts
@@ -160,7 +160,7 @@ export async function mono_wasm_init_threads () {
}
// when we create threads with browser event loop, it's not able to be joined by mono's thread join during shutdown and blocks process exit
-export function cancelThreads () {
+export function postCancelThreads () {
if (!WasmEnableThreads) return;
const workers: PThreadWorker[] = getRunningWorkers();
for (const worker of workers) {
@@ -313,6 +313,10 @@ export function getRunningWorkers (): PThreadWorker[] {
return getModulePThread().runningWorkers;
}
+export function terminateAllThreads (): void {
+ getModulePThread().terminateAllThreads();
+}
+
export function loadWasmModuleToWorker (worker: PThreadWorker): Promise {
return getModulePThread().loadWasmModuleToWorker(worker);
}
diff --git a/src/mono/browser/runtime/pthreads/worker-interop.ts b/src/mono/browser/runtime/pthreads/worker-interop.ts
new file mode 100644
index 00000000000000..35a8ff61a8a1fa
--- /dev/null
+++ b/src/mono/browser/runtime/pthreads/worker-interop.ts
@@ -0,0 +1,45 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import WasmEnableThreads from "consts:wasmEnableThreads";
+
+import type { GCHandle } from "../types/internal";
+
+import { ENVIRONMENT_IS_PTHREAD, Module, mono_assert, runtimeHelpers } from "../globals";
+import { bindings_init } from "../startup";
+import { forceDisposeProxies } from "../gc-handles";
+import { GCHandleNull, WorkerToMainMessageType } from "../types/internal";
+import { monoThreadInfo, postMessageToMain, update_thread_info } from "./shared";
+
+export function mono_wasm_install_js_worker_interop (context_gc_handle: GCHandle): void {
+ if (!WasmEnableThreads) return;
+ bindings_init();
+ mono_assert(!runtimeHelpers.proxyGCHandle, "JS interop should not be already installed on this worker.");
+ runtimeHelpers.proxyGCHandle = context_gc_handle;
+ if (ENVIRONMENT_IS_PTHREAD) {
+ runtimeHelpers.managedThreadTID = runtimeHelpers.currentThreadTID;
+ runtimeHelpers.isManagedRunningOnCurrentThread = true;
+ }
+ Module.runtimeKeepalivePush();
+ monoThreadInfo.isDirtyBecauseOfInterop = true;
+ update_thread_info();
+ if (ENVIRONMENT_IS_PTHREAD) {
+ postMessageToMain({
+ monoCmd: WorkerToMainMessageType.enabledInterop,
+ info: monoThreadInfo,
+ });
+ }
+}
+
+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.proxyGCHandle, "JSSynchronizationContext is not installed on this worker.");
+
+ forceDisposeProxies(true, runtimeHelpers.diagnosticTracing);
+ Module.runtimeKeepalivePop();
+
+ runtimeHelpers.proxyGCHandle = GCHandleNull;
+ runtimeHelpers.mono_wasm_bindings_is_ready = false;
+ update_thread_info();
+}
diff --git a/src/mono/browser/runtime/run.ts b/src/mono/browser/runtime/run.ts
index be412fb9b70230..21f5fa2131a34c 100644
--- a/src/mono/browser/runtime/run.ts
+++ b/src/mono/browser/runtime/run.ts
@@ -3,12 +3,12 @@
import WasmEnableThreads from "consts:wasmEnableThreads";
-import { ENVIRONMENT_IS_NODE, Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals";
+import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_WORKER, Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals";
import { mono_wasm_wait_for_debugger } from "./debug";
import { mono_wasm_set_main_args } from "./startup";
import cwraps from "./cwraps";
-import { mono_log_info } from "./logging";
-import { cancelThreads } from "./pthreads";
+import { mono_log_error, mono_log_info, mono_wasm_stringify_as_error_with_stack } from "./logging";
+import { postCancelThreads, terminateAllThreads } from "./pthreads";
import { call_entry_point } from "./managed-exports";
/**
@@ -77,16 +77,35 @@ export async function mono_run_main (main_assembly_name?: string, args?: string[
export function nativeExit (code: number) {
- if (WasmEnableThreads) {
- cancelThreads();
+ if (runtimeHelpers.runtimeReady) {
+ runtimeHelpers.runtimeReady = false;
+ if (WasmEnableThreads) {
+ postCancelThreads();
+ }
+ cwraps.mono_wasm_exit(code);
}
- cwraps.mono_wasm_exit(code);
}
export function nativeAbort (reason: any) {
loaderHelpers.exitReason = reason;
- if (!loaderHelpers.is_exited()) {
- cwraps.mono_wasm_abort();
+ if (runtimeHelpers.runtimeReady) {
+ runtimeHelpers.runtimeReady = false;
+ if (WasmEnableThreads) {
+ if (!ENVIRONMENT_IS_WORKER) {
+ terminateAllThreads();
+ } else {
+ // just in case if the UI thread is blocked, we need to force exit
+ // if UI thread receives message from Module.abort below, this thread will be terminated earlier
+ setTimeout(() => {
+ mono_log_error("forcing abort 3000ms after nativeAbort attempt", reason);
+ // _emscripten_force_exit is proxied to UI thread and should also arrive in spin wait loop
+ Module._emscripten_force_exit(1);
+ }, 3000);
+ }
+ }
+
+ const reasonString = mono_wasm_stringify_as_error_with_stack(reason);
+ Module.abort(reasonString);
}
throw reason;
}
diff --git a/src/mono/browser/runtime/startup.ts b/src/mono/browser/runtime/startup.ts
index 22fd1b085ee80a..b89522caff198a 100644
--- a/src/mono/browser/runtime/startup.ts
+++ b/src/mono/browser/runtime/startup.ts
@@ -203,6 +203,8 @@ export function preRunWorker () {
const mark = startMeasure();
try {
jiterpreter_allocate_tables(); // this will return quickly if already allocated
+ runtimeHelpers.nativeExit = nativeExit;
+ runtimeHelpers.nativeAbort = nativeAbort;
runtimeHelpers.runtimeReady = true;
// signal next stage
runtimeHelpers.afterPreRun.promise_control.resolve();
diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts
index 25dce9f66bb751..e5579bfeba0f8c 100644
--- a/src/mono/browser/runtime/types/internal.ts
+++ b/src/mono/browser/runtime/types/internal.ts
@@ -433,6 +433,8 @@ export declare interface EmscriptenModuleInternal {
__emscripten_thread_init(pthread_ptr: PThreadPtr, isMainBrowserThread: number, isMainRuntimeThread: number, canBlock: number): void;
print(message: string): void;
printErr(message: string): void;
+ abort(reason: any): void;
+ _emscripten_force_exit(exit_code: number): void;
}
/// A PromiseController encapsulates a Promise together with easy access to its resolve and reject functions.
@@ -551,6 +553,7 @@ export interface PThreadLibrary {
threadInitTLS: () => void,
getNewWorker: () => PThreadWorker,
returnWorkerToPool: (worker: PThreadWorker) => void,
+ terminateAllThreads: () => void,
}
export interface PThreadInfoMap {
diff --git a/src/mono/browser/test-main.js b/src/mono/browser/test-main.js
index 1feb21ef2f796b..f34be2644aebec 100644
--- a/src/mono/browser/test-main.js
+++ b/src/mono/browser/test-main.js
@@ -52,7 +52,7 @@ if (!ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WEB && typeof globalThis.crypto ===
}
if (ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_WORKER) {
- console.log("Running at: " + globalThis.location.href);
+ console.log("Running '" + globalThis.navigator.userAgent + "' at: " + globalThis.location.href);
}
let v8args;