Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[browser][MT] improve abort on web worker #100610

Merged
merged 5 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/mono/browser/browser.proj
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@
<EmccExportedFunction Include="stackAlloc" />
<EmccExportedFunction Include="stackRestore" />
<EmccExportedFunction Include="stackSave" />
<EmccExportedFunction Include="_emscripten_force_exit" />
</ItemGroup>
<!-- for the jiterpreter -->
<ItemGroup>
Expand Down
2 changes: 0 additions & 2 deletions src/mono/browser/runtime/cwraps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 0 additions & 6 deletions src/mono/browser/runtime/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -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[])
{
Expand Down
2 changes: 1 addition & 1 deletion src/mono/browser/runtime/invoke-js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
64 changes: 41 additions & 23 deletions src/mono/browser/runtime/loader/exit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -82,23 +86,27 @@ 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) {
reason.message = message;
}

// 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);
Expand All @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
7 changes: 3 additions & 4 deletions src/mono/browser/runtime/loader/logging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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);
}
};
Expand Down
39 changes: 34 additions & 5 deletions src/mono/browser/runtime/logging.ts
Original file line number Diff line number Diff line change
@@ -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";

Expand All @@ -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);
Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions src/mono/browser/runtime/managed-exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}
}
Expand Down
7 changes: 4 additions & 3 deletions src/mono/browser/runtime/pthreads/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
39 changes: 2 additions & 37 deletions src/mono/browser/runtime/pthreads/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -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;
Expand Down
Loading
Loading