Skip to content

Commit 552b5e9

Browse files
authored
[browser][MT] improve abort on web worker (#100610)
1 parent 001d60a commit 552b5e9

16 files changed

+171
-93
lines changed

src/mono/browser/browser.proj

+1
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@
207207
<EmccExportedFunction Include="stackAlloc" />
208208
<EmccExportedFunction Include="stackRestore" />
209209
<EmccExportedFunction Include="stackSave" />
210+
<EmccExportedFunction Include="_emscripten_force_exit" />
210211
</ItemGroup>
211212
<!-- for the jiterpreter -->
212213
<ItemGroup>

src/mono/browser/runtime/cwraps.ts

-2
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ const fn_signatures: SigLine[] = [
6363
[true, "mono_wasm_intern_string_ref", "void", ["number"]],
6464

6565
[false, "mono_wasm_exit", "void", ["number"]],
66-
[false, "mono_wasm_abort", "void", []],
6766
[true, "mono_wasm_getenv", "number", ["string"]],
6867
[true, "mono_wasm_set_main_args", "void", ["number", "number"]],
6968
// These two need to be lazy because they may be missing
@@ -192,7 +191,6 @@ export interface t_Cwraps {
192191
mono_wasm_intern_string_ref(strRef: MonoStringRef): void;
193192

194193
mono_wasm_exit(exit_code: number): void;
195-
mono_wasm_abort(): void;
196194
mono_wasm_getenv(name: string): CharPtr;
197195
mono_wasm_set_main_args(argc: number, argv: VoidPtr): void;
198196
mono_wasm_exec_regression(verbose_level: number, image: string): number;

src/mono/browser/runtime/driver.c

-6
Original file line numberDiff line numberDiff line change
@@ -347,12 +347,6 @@ mono_wasm_exit (int exit_code)
347347
emscripten_force_exit (exit_code);
348348
}
349349

350-
EMSCRIPTEN_KEEPALIVE int
351-
mono_wasm_abort ()
352-
{
353-
abort ();
354-
}
355-
356350
EMSCRIPTEN_KEEPALIVE void
357351
mono_wasm_set_main_args (int argc, char* argv[])
358352
{

src/mono/browser/runtime/invoke-js.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export function mono_wasm_invoke_jsimport_MT (signature: JSFunctionSignature, ar
6060
}
6161
return;
6262
} catch (ex2: any) {
63-
runtimeHelpers.nativeExit(ex2);
63+
runtimeHelpers.nativeAbort(ex2);
6464
return;
6565
}
6666
}

src/mono/browser/runtime/loader/exit.ts

+41-23
Original file line numberDiff line numberDiff line change
@@ -39,36 +39,40 @@ export function uninstallUnhandledErrorHandler () {
3939
}
4040
}
4141

42+
let originalOnAbort: ((reason: any, extraJson?:string)=>void)|undefined;
43+
let originalOnExit: ((code: number)=>void)|undefined;
44+
4245
export function registerEmscriptenExitHandlers () {
43-
if (!emscriptenModule.onAbort) {
44-
emscriptenModule.onAbort = onAbort;
45-
}
46-
if (!emscriptenModule.onExit) {
47-
emscriptenModule.onExit = onExit;
48-
}
46+
originalOnAbort = emscriptenModule.onAbort;
47+
originalOnExit = emscriptenModule.onExit;
48+
emscriptenModule.onAbort = onAbort;
49+
emscriptenModule.onExit = onExit;
4950
}
5051

5152
function unregisterEmscriptenExitHandlers () {
5253
if (emscriptenModule.onAbort == onAbort) {
53-
emscriptenModule.onAbort = undefined;
54+
emscriptenModule.onAbort = originalOnAbort;
5455
}
5556
if (emscriptenModule.onExit == onExit) {
56-
emscriptenModule.onExit = undefined;
57+
emscriptenModule.onExit = originalOnExit;
5758
}
5859
}
5960
function onExit (code: number) {
61+
if (originalOnExit) {
62+
originalOnExit(code);
63+
}
6064
mono_exit(code, loaderHelpers.exitReason);
6165
}
6266

6367
function onAbort (reason: any) {
64-
mono_exit(1, loaderHelpers.exitReason || reason);
68+
if (originalOnAbort) {
69+
originalOnAbort(reason || loaderHelpers.exitReason);
70+
}
71+
mono_exit(1, reason || loaderHelpers.exitReason);
6572
}
6673

6774
// this will also call mono_wasm_exit if available, which will call exitJS -> _proc_exit -> terminateAllThreads
6875
export function mono_exit (exit_code: number, reason?: any): void {
69-
unregisterEmscriptenExitHandlers();
70-
uninstallUnhandledErrorHandler();
71-
7276
// unify shape of the reason object
7377
const is_object = reason && typeof reason === "object";
7478
exit_code = (is_object && typeof reason.status === "number")
@@ -82,23 +86,27 @@ export function mono_exit (exit_code: number, reason?: any): void {
8286
reason = is_object
8387
? reason
8488
: (runtimeHelpers.ExitStatus
85-
? new runtimeHelpers.ExitStatus(exit_code)
89+
? createExitStatus(exit_code, message)
8690
: new Error("Exit with code " + exit_code + " " + message));
8791
reason.status = exit_code;
8892
if (!reason.message) {
8993
reason.message = message;
9094
}
9195

9296
// force stack property to be generated before we shut down managed code, or create current stack if it doesn't exist
93-
if (!reason.stack) {
94-
reason.stack = new Error().stack || "";
95-
}
97+
const stack = "" + (reason.stack || (new Error().stack));
98+
Object.defineProperty(reason, "stack", {
99+
get: () => stack
100+
});
96101

97102
// don't report this error twice
103+
const alreadySilent = !!reason.silent;
98104
reason.silent = true;
99105

100106
if (!is_exited()) {
101107
try {
108+
unregisterEmscriptenExitHandlers();
109+
uninstallUnhandledErrorHandler();
102110
if (!runtimeHelpers.runtimeReady) {
103111
mono_log_debug("abort_startup, reason: " + reason);
104112
abort_promises(reason);
@@ -119,19 +127,25 @@ export function mono_exit (exit_code: number, reason?: any): void {
119127
}
120128

121129
try {
122-
logOnExit(exit_code, reason);
123-
appendElementOnExit(exit_code);
130+
if (!alreadySilent) {
131+
logOnExit(exit_code, reason);
132+
appendElementOnExit(exit_code);
133+
}
124134
} catch (err) {
125135
mono_log_warn("mono_exit failed", err);
126136
// don't propagate any failures
127137
}
128138

129139
loaderHelpers.exitCode = exit_code;
130-
loaderHelpers.exitReason = reason.message;
140+
if (!loaderHelpers.exitReason) {
141+
loaderHelpers.exitReason = reason;
142+
}
131143

132144
if (!ENVIRONMENT_IS_WORKER && runtimeHelpers.runtimeReady) {
133145
emscriptenModule.runtimeKeepalivePop();
134146
}
147+
} else {
148+
mono_log_debug("mono_exit called after exit");
135149
}
136150

137151
if (loaderHelpers.config && loaderHelpers.config.asyncFlushOnExit && exit_code === 0) {
@@ -154,13 +168,11 @@ export function mono_exit (exit_code: number, reason?: any): void {
154168
function set_exit_code_and_quit_now (exit_code: number, reason?: any): void {
155169
if (WasmEnableThreads && ENVIRONMENT_IS_WORKER && runtimeHelpers.runtimeReady && runtimeHelpers.nativeAbort) {
156170
// note that the reason is not passed to UI thread
157-
runtimeHelpers.runtimeReady = false;
158171
runtimeHelpers.nativeAbort(reason);
159172
throw reason;
160173
}
161174

162175
if (runtimeHelpers.runtimeReady && runtimeHelpers.nativeExit) {
163-
runtimeHelpers.runtimeReady = false;
164176
try {
165177
runtimeHelpers.nativeExit(exit_code);
166178
} catch (error: any) {
@@ -205,7 +217,6 @@ async function flush_node_streams () {
205217
}
206218

207219
function abort_promises (reason: any) {
208-
loaderHelpers.exitReason = reason;
209220
loaderHelpers.allDownloadsQueued.promise_control.reject(reason);
210221
loaderHelpers.afterConfigLoaded.promise_control.reject(reason);
211222
loaderHelpers.wasmCompilePromise.promise_control.reject(reason);
@@ -256,7 +267,7 @@ function logOnExit (exit_code: number, reason: any) {
256267
}
257268
}
258269
}
259-
if (loaderHelpers.config) {
270+
if (!ENVIRONMENT_IS_WORKER && loaderHelpers.config) {
260271
if (loaderHelpers.config.logExitCode) {
261272
if (loaderHelpers.config.forwardConsoleLogsToWS) {
262273
teardown_proxy_console("WASM EXIT " + exit_code);
@@ -294,3 +305,10 @@ function fatal_handler (event: any, reason: any, type: string) {
294305
// no not re-throw from the fatal handler
295306
}
296307
}
308+
309+
function createExitStatus (status:number, message:string) {
310+
const ex = new runtimeHelpers.ExitStatus(status);
311+
ex.message = message;
312+
ex.toString = () => message;
313+
return ex;
314+
}

src/mono/browser/runtime/loader/logging.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,6 @@ export function mono_log_error (msg: string, ...data: any) {
4242
if (data[0].silent) {
4343
return;
4444
}
45-
if (data[0].toString) {
46-
console.error(prefix + msg, data[0].toString());
47-
}
4845
if (data[0].toString) {
4946
console.error(prefix + msg, data[0].toString());
5047
return;
@@ -118,12 +115,13 @@ export function setup_proxy_console (id: string, console: Console, origin: strin
118115
}
119116

120117
export function teardown_proxy_console (message?: string) {
118+
let counter = 30;
121119
const stop_when_ws_buffer_empty = () => {
122120
if (!consoleWebSocket) {
123121
if (message && originalConsoleMethods) {
124122
originalConsoleMethods.log(message);
125123
}
126-
} else if (consoleWebSocket.bufferedAmount == 0) {
124+
} else if (consoleWebSocket.bufferedAmount == 0 || counter == 0) {
127125
if (message) {
128126
// tell xharness WasmTestMessagesProcessor we are done.
129127
// note this sends last few bytes into the same WS
@@ -136,6 +134,7 @@ export function teardown_proxy_console (message?: string) {
136134
consoleWebSocket.close(1000, message);
137135
(consoleWebSocket as any) = undefined;
138136
} else {
137+
counter--;
139138
globalThis.setTimeout(stop_when_ws_buffer_empty, 100);
140139
}
141140
};

src/mono/browser/runtime/logging.ts

+34-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
/* eslint-disable no-console */
5-
import { INTERNAL, runtimeHelpers, mono_assert } from "./globals";
4+
import WasmEnableThreads from "consts:wasmEnableThreads";
5+
6+
import { threads_c_functions as tcwraps } from "./cwraps";
7+
import { INTERNAL, runtimeHelpers, mono_assert, loaderHelpers, ENVIRONMENT_IS_WORKER, Module } from "./globals";
68
import { utf8ToString } from "./strings";
79
import { CharPtr, VoidPtr } from "./types/emscripten";
810

@@ -12,6 +14,7 @@ export function set_thread_prefix (threadPrefix: string) {
1214
prefix = `[${threadPrefix}] MONO_WASM: `;
1315
}
1416

17+
/* eslint-disable no-console */
1518
export function mono_log_debug (msg: string, ...data: any) {
1619
if (runtimeHelpers.diagnosticTracing) {
1720
console.debug(prefix + msg, ...data);
@@ -27,9 +30,15 @@ export function mono_log_warn (msg: string, ...data: any) {
2730
}
2831

2932
export function mono_log_error (msg: string, ...data: any) {
30-
if (data && data.length > 0 && data[0] && typeof data[0] === "object" && data[0].silent) {
33+
if (data && data.length > 0 && data[0] && typeof data[0] === "object") {
3134
// don't log silent errors
32-
return;
35+
if (data[0].silent) {
36+
return;
37+
}
38+
if (data[0].toString) {
39+
console.error(prefix + msg, data[0].toString());
40+
return;
41+
}
3342
}
3443
console.error(prefix + msg, ...data);
3544
}
@@ -123,7 +132,27 @@ export function mono_wasm_trace_logger (log_domain_ptr: CharPtr, log_level_ptr:
123132
switch (log_level) {
124133
case "critical":
125134
case "error":
126-
console.error(mono_wasm_stringify_as_error_with_stack(message));
135+
{
136+
const messageWithStack = message + "\n" + (new Error().stack);
137+
if (!loaderHelpers.exitReason) {
138+
loaderHelpers.exitReason = messageWithStack;
139+
}
140+
console.error(mono_wasm_stringify_as_error_with_stack(messageWithStack));
141+
if (WasmEnableThreads) {
142+
try {
143+
tcwraps.mono_wasm_print_thread_dump();
144+
} catch (e) {
145+
console.error("Failed to print thread dump", e);
146+
}
147+
}
148+
if (WasmEnableThreads && ENVIRONMENT_IS_WORKER) {
149+
setTimeout(() => {
150+
mono_log_error("forcing abort 3000ms after last error log message", messageWithStack);
151+
// _emscripten_force_exit is proxied to UI thread and should also arrive in spin wait loop
152+
Module._emscripten_force_exit(1);
153+
}, 3000);
154+
}
155+
}
127156
break;
128157
case "warning":
129158
console.warn(message);

src/mono/browser/runtime/managed-exports.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { assert_c_interop, assert_js_interop } from "./invoke-js";
1414
import { monoThreadInfo, mono_wasm_main_thread_ptr } from "./pthreads";
1515
import { _zero_region, copyBytes } from "./memory";
1616
import { stringToUTF8Ptr } from "./strings";
17-
import { mono_log_debug } from "./logging";
17+
import { mono_log_error } from "./logging";
1818

1919
const managedExports: ManagedExports = {} as any;
2020

@@ -269,7 +269,7 @@ export function install_main_synchronization_context (jsThreadBlockingMode: JSTh
269269
}
270270
return get_arg_gc_handle(res) as any;
271271
} catch (e) {
272-
mono_log_debug("install_main_synchronization_context failed", e);
272+
mono_log_error("install_main_synchronization_context failed", e);
273273
throw e;
274274
}
275275
}

src/mono/browser/runtime/pthreads/index.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ import { mono_log_warn } from "../logging";
55
import { utf16ToString } from "../strings";
66

77
export {
8-
mono_wasm_main_thread_ptr, mono_wasm_install_js_worker_interop, mono_wasm_uninstall_js_worker_interop,
8+
mono_wasm_main_thread_ptr,
99
mono_wasm_pthread_ptr, update_thread_info, isMonoThreadMessage, monoThreadInfo,
1010
} from "./shared";
11+
export { mono_wasm_install_js_worker_interop, mono_wasm_uninstall_js_worker_interop } from "./worker-interop";
1112
export {
12-
mono_wasm_dump_threads, cancelThreads,
13+
mono_wasm_dump_threads, postCancelThreads,
1314
populateEmscriptenPool, mono_wasm_init_threads,
14-
waitForThread, replaceEmscriptenPThreadUI
15+
waitForThread, replaceEmscriptenPThreadUI, terminateAllThreads,
1516
} from "./ui-thread";
1617
export {
1718
mono_wasm_pthread_on_pthread_attached, mono_wasm_pthread_on_pthread_unregistered,

src/mono/browser/runtime/pthreads/shared.ts

+2-37
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@ import BuildConfiguration from "consts:configuration";
66

77
import type { GCHandle, MonoThreadMessage, PThreadInfo, PThreadPtr } from "../types/internal";
88

9-
import { ENVIRONMENT_IS_PTHREAD, Module, loaderHelpers, mono_assert, runtimeHelpers } from "../globals";
9+
import { Module, loaderHelpers, runtimeHelpers } from "../globals";
1010
import { set_thread_prefix } from "../logging";
11-
import { bindings_init } from "../startup";
12-
import { forceDisposeProxies } from "../gc-handles";
13-
import { monoMessageSymbol, GCHandleNull, PThreadPtrNull, WorkerToMainMessageType } from "../types/internal";
11+
import { monoMessageSymbol, PThreadPtrNull, WorkerToMainMessageType } from "../types/internal";
1412
import { threads_c_functions as tcwraps } from "../cwraps";
1513
import { forceThreadMemoryViewRefresh } from "../memory";
1614

@@ -34,39 +32,6 @@ export function isMonoThreadMessage (x: unknown): x is MonoThreadMessage {
3432
return typeof (xmsg.type) === "string" && typeof (xmsg.cmd) === "string";
3533
}
3634

37-
export function mono_wasm_install_js_worker_interop (context_gc_handle: GCHandle): void {
38-
if (!WasmEnableThreads) return;
39-
bindings_init();
40-
mono_assert(!runtimeHelpers.proxyGCHandle, "JS interop should not be already installed on this worker.");
41-
runtimeHelpers.proxyGCHandle = context_gc_handle;
42-
if (ENVIRONMENT_IS_PTHREAD) {
43-
runtimeHelpers.managedThreadTID = runtimeHelpers.currentThreadTID;
44-
runtimeHelpers.isManagedRunningOnCurrentThread = true;
45-
}
46-
Module.runtimeKeepalivePush();
47-
monoThreadInfo.isDirtyBecauseOfInterop = true;
48-
update_thread_info();
49-
if (ENVIRONMENT_IS_PTHREAD) {
50-
postMessageToMain({
51-
monoCmd: WorkerToMainMessageType.enabledInterop,
52-
info: monoThreadInfo,
53-
});
54-
}
55-
}
56-
57-
export function mono_wasm_uninstall_js_worker_interop (): void {
58-
if (!WasmEnableThreads) return;
59-
mono_assert(runtimeHelpers.mono_wasm_bindings_is_ready, "JS interop is not installed on this worker.");
60-
mono_assert(runtimeHelpers.proxyGCHandle, "JSSynchronizationContext is not installed on this worker.");
61-
62-
forceDisposeProxies(true, runtimeHelpers.diagnosticTracing);
63-
Module.runtimeKeepalivePop();
64-
65-
runtimeHelpers.proxyGCHandle = GCHandleNull;
66-
runtimeHelpers.mono_wasm_bindings_is_ready = false;
67-
update_thread_info();
68-
}
69-
7035
// this is just for Debug build of the runtime, making it easier to debug worker threads
7136
export function update_thread_info (): void {
7237
if (!WasmEnableThreads) return;

0 commit comments

Comments
 (0)