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] logging and exit improvements #93472

Merged
merged 8 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from 7 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
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices.JavaScript;

#pragma warning disable CS0612 // using obsolete members until we finish https://github.com/dotnet/runtime/pull/66304/
#pragma warning disable IDE0060

namespace System
{
internal sealed class WasmConsoleStream : ConsoleStream
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ export async function runSecondRuntimeAndTestStaticState() {
const { dotnet: dotnet2 } = await import('./_framework/dotnet.js?instance=2');
const runtime2 = await dotnet2
.withStartupMemoryCache(false)
.withConfig({
forwardConsoleLogsToWS: false,
diagnosticTracing: false,
appendElementOnExit: false,
logExitCode: false,
})
.create();

const increment1 = await getIncrementStateFunction(App.runtime);
Expand Down
4 changes: 3 additions & 1 deletion src/mono/wasm/runtime/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -1186,7 +1186,9 @@ EMSCRIPTEN_KEEPALIVE int
mono_wasm_exit (int exit_code)
{
mono_jit_cleanup (root_domain);
exit (exit_code);
fflush (stdout);
fflush (stderr);
emscripten_force_exit (exit_code);
}

EMSCRIPTEN_KEEPALIVE int
Expand Down
2 changes: 0 additions & 2 deletions src/mono/wasm/runtime/es6/dotnet.es6.lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ function setup(linkerSetup) {
updateMemoryViews,
pthreadReplacements,
scriptDirectory,
noExitRuntime
};
// USE_PTHREADS is emscripten's define symbol, which is passed to acorn optimizer, so we could use it here
#if USE_PTHREADS
Expand All @@ -39,7 +38,6 @@ function setup(linkerSetup) {
ENVIRONMENT_IS_WORKER = dotnet_replacements.ENVIRONMENT_IS_WORKER;
Module.__dotnet_runtime.initializeReplacements(dotnet_replacements);
updateMemoryViews = dotnet_replacements.updateMemoryViews;
noExitRuntime = dotnet_replacements.noExitRuntime;
fetch = dotnet_replacements.fetch;
require = dotnet_replacements.require;
_scriptDir = __dirname = scriptDirectory = dotnet_replacements.scriptDirectory;
Expand Down
108 changes: 79 additions & 29 deletions src/mono/wasm/runtime/loader/exit.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_WEB, INTERNAL, loaderHelpers, mono_assert, runtimeHelpers } from "./globals";
import { mono_log_debug, consoleWebSocket, mono_log_error, mono_log_info_no_prefix, mono_log_warn } from "./logging";
import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_WEB, ENVIRONMENT_IS_WORKER, INTERNAL, emscriptenModule, loaderHelpers, mono_assert, runtimeHelpers } from "./globals";
import { mono_log_debug, mono_log_error, mono_log_info_no_prefix, mono_log_warn, teardown_proxy_console } from "./logging";

export function is_exited() {
return loaderHelpers.exitCode !== undefined;
Expand All @@ -14,11 +14,39 @@ export function is_runtime_running() {

export function assert_runtime_running() {
mono_assert(runtimeHelpers.runtimeReady, "mono runtime didn't start yet");
mono_assert(!loaderHelpers.assertAfterExit || !is_exited(), () => `mono runtime already exited with ${loaderHelpers.exitCode}`);
mono_assert(!loaderHelpers.assertAfterExit || !is_exited(), () => `mono runtime already exited with ${loaderHelpers.exitCode} ${loaderHelpers.exitReason}`);
}

export function register_exit_handlers() {
if (!emscriptenModule.onAbort) {
emscriptenModule.onAbort = onAbort;
}
if (!emscriptenModule.onExit) {
emscriptenModule.onExit = onExit;
}
}

export function unregister_exit_handlers() {
if (emscriptenModule.onAbort == onAbort) {
emscriptenModule.onAbort = undefined;
}
if (emscriptenModule.onExit == onExit) {
emscriptenModule.onExit = undefined;
}
}

function onExit(code: number) {
mono_exit(code, loaderHelpers.exitReason);
}

function onAbort(reason: any) {
mono_exit(1, loaderHelpers.exitReason || reason);
}

// 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 {
unregister_exit_handlers();

// unify shape of the reason object
const is_object = reason && typeof reason === "object";
exit_code = (is_object && typeof reason.status === "number") ? reason.status : exit_code;
Expand Down Expand Up @@ -48,20 +76,35 @@ export function mono_exit(exit_code: number, reason?: any): void {
if (!runtimeHelpers.runtimeReady) {
mono_log_debug("abort_startup, reason: " + reason);
abort_promises(reason);
} else {
if (runtimeHelpers.jiterpreter_dump_stats) {
runtimeHelpers.jiterpreter_dump_stats(false);
}
if (exit_code === 0 && loaderHelpers.config?.interopCleanupOnExit) {
runtimeHelpers.forceDisposeProxies(true, true);
}
}
}
catch (err) {
mono_log_warn("mono_exit failed", err);
// don't propagate any failures
}

try {
logOnExit(exit_code, reason);
appendElementOnExit(exit_code);
if (runtimeHelpers.jiterpreter_dump_stats) runtimeHelpers.jiterpreter_dump_stats(false);
if (exit_code === 0 && loaderHelpers.config?.interopCleanupOnExit) {
runtimeHelpers.forceDisposeProxies(true, true);
}
}
catch (err) {
mono_log_warn("mono_exit failed", err);
// don't propagate any failures
}

loaderHelpers.exitCode = exit_code;
loaderHelpers.exitReason = reason.message;

if (!ENVIRONMENT_IS_WORKER && runtimeHelpers.runtimeReady) {
emscriptenModule.runtimeKeepalivePop();
}
}

if (loaderHelpers.config && loaderHelpers.config.asyncFlushOnExit && exit_code === 0) {
Expand All @@ -83,8 +126,16 @@ export function mono_exit(exit_code: number, reason?: any): void {
}

function set_exit_code_and_quit_now(exit_code: number, reason?: any): void {
if (is_runtime_running() && runtimeHelpers.mono_wasm_exit) {
runtimeHelpers.mono_wasm_exit(exit_code);
if (runtimeHelpers.runtimeReady && runtimeHelpers.mono_wasm_exit) {
runtimeHelpers.runtimeReady = false;
try {
runtimeHelpers.mono_wasm_exit(exit_code);
}
catch (err) {
if (runtimeHelpers.ExitStatus && !(err instanceof runtimeHelpers.ExitStatus)) {
mono_log_warn("mono_wasm_exit failed", err);
}
}
}
// just in case mono_wasm_exit didn't exit or throw
if (exit_code !== 0 || !ENVIRONMENT_IS_WEB) {
Expand All @@ -105,19 +156,25 @@ async function flush_node_streams() {
const process = await import(/* webpackIgnore: true */"process");
const flushStream = (stream: any) => {
return new Promise<void>((resolve, reject) => {
stream.on("error", (error: any) => reject(error));
stream.write("", function () { resolve(); });
stream.on("error", reject);
stream.end("", "utf8", resolve);
});
};
const stderrFlushed = flushStream(process.stderr);
const stdoutFlushed = flushStream(process.stdout);
await Promise.all([stdoutFlushed, stderrFlushed]);
let timeoutId;
const timeout = new Promise(resolve => {
timeoutId = setTimeout(() => resolve("timeout"), 1000);
});
await Promise.race([Promise.all([stdoutFlushed, stderrFlushed]), timeout]);
clearTimeout(timeoutId);
} catch (err) {
mono_log_error(`flushing std* streams failed: ${err}`);
}
}

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 @@ -167,23 +224,16 @@ function logOnExit(exit_code: number, reason: any) {
mono_log(JSON.stringify(reason));
}
}
if (loaderHelpers.config && loaderHelpers.config.logExitCode) {
if (consoleWebSocket) {
const stop_when_ws_buffer_empty = () => {
if (consoleWebSocket.bufferedAmount == 0) {
// tell xharness WasmTestMessagesProcessor we are done.
// note this sends last few bytes into the same WS
mono_log_info_no_prefix("WASM EXIT " + exit_code);
consoleWebSocket.onclose = null;
consoleWebSocket.close(1000, "exit_code:" + exit_code + ": " + reason);
}
else {
globalThis.setTimeout(stop_when_ws_buffer_empty, 100);
}
};
stop_when_ws_buffer_empty();
} else {
mono_log_info_no_prefix("WASM EXIT " + exit_code);
if (loaderHelpers.config) {
if (loaderHelpers.config.logExitCode) {
if (loaderHelpers.config.forwardConsoleLogsToWS) {
teardown_proxy_console("WASM EXIT " + exit_code);
} else {
mono_log_info_no_prefix("WASM EXIT " + exit_code);
}
}
else if (loaderHelpers.config.forwardConsoleLogsToWS) {
teardown_proxy_console();
}
}
}
Loading
Loading