diff --git a/src/mono/browser/browser.proj b/src/mono/browser/browser.proj index 38d6f035e6cf3..a2ac05d51b730 100644 --- a/src/mono/browser/browser.proj +++ b/src/mono/browser/browser.proj @@ -206,6 +206,7 @@ + diff --git a/src/mono/browser/runtime/cwraps.ts b/src/mono/browser/runtime/cwraps.ts index 3ce3302e3b351..ed64d5c541d4b 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 bca878eb1820d..1b3663e4f3f3c 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 07c02c719d485..fa6949f2d636a 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 2264cd086fbd7..cdea5d5997265 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 668d7e667b6d3..ce98616e08d0e 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 1d91a870d52fb..3558f24dc6c01 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 065136faaba47..79bbcbf2af964 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 25f981d3f3b69..87ed1b006184d 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 f72804fbcf873..c266c2dfec66e 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 b5f4f72875ec1..0dc2b80c57e0f 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 0000000000000..35a8ff61a8a1f --- /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 be412fb9b7023..21f5fa2131a34 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 22fd1b085ee80..b89522caff198 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 25dce9f66bb75..e5579bfeba0f8 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 1feb21ef2f796..f34be2644aebe 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;