Skip to content

Commit

Permalink
- more testing if the runtime is still running
Browse files Browse the repository at this point in the history
- testing if the promise hoilder is already disposed
- added `exit` to `RuntimeAPI`
- unregister from `FinalizationRegistry` in `upgrade_managed_proxy_to_strong_ref`
- improve `BlazorHosted` WBT exit
  • Loading branch information
pavelsavara committed Mar 7, 2024
1 parent b0f6444 commit ee24988
Show file tree
Hide file tree
Showing 8 changed files with 42 additions and 10 deletions.
29 changes: 20 additions & 9 deletions src/mono/browser/runtime/cancelable-promise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ export function wrap_as_cancelable<T>(inner: Promise<T>): ControllablePromise<T>
}

export function mono_wasm_cancel_promise(task_holder_gc_handle: GCHandle): void {
if (!loaderHelpers.is_runtime_running()) {
mono_log_debug("This promise can't be canceled, mono runtime already exited.");
return;
}
const holder = _lookup_js_owned_object(task_holder_gc_handle) as PromiseHolder;
mono_assert(!!holder, () => `Expected Promise for GCHandle ${task_holder_gc_handle}`);
holder.cancel();
Expand Down Expand Up @@ -75,6 +79,11 @@ export class PromiseHolder extends ManagedObject {

resolve(data: any) {
mono_assert(!this.isResolved, "resolve could be called only once");
mono_assert(!this.isDisposed, "resolve is already disposed.");
if (!loaderHelpers.is_runtime_running()) {
mono_log_debug("This promise resolution can't be propagated to managed code, mono runtime already exited.");
return;
}
if (WasmEnableThreads && !this.setIsResolving()) {
// we know that cancelation is in flight
// because we need to keep the GCHandle alive until until the cancelation arrives
Expand All @@ -89,11 +98,16 @@ export class PromiseHolder extends ManagedObject {
return;
}
this.isResolved = true;
this.complete_task(data, null);
this.complete_task_wrapper(data, null);
}

reject(reason: any) {
mono_assert(!this.isResolved, "reject could be called only once");
mono_assert(!this.isDisposed, "resolve is already disposed.");
if (!loaderHelpers.is_runtime_running()) {
mono_log_debug("This promise rejection can't be propagated to managed code, mono runtime already exited.");
return;
}
const isCancelation = reason && reason[promise_holder_symbol] === this;
if (WasmEnableThreads && !isCancelation && !this.setIsResolving()) {
// we know that cancelation is in flight
Expand All @@ -109,21 +123,22 @@ export class PromiseHolder extends ManagedObject {
return;
}
this.isResolved = true;
this.complete_task(null, reason);
this.complete_task_wrapper(null, reason);
}

cancel() {
mono_assert(!this.isResolved, "cancel could be called only once");
mono_assert(!this.isDisposed, "resolve is already disposed.");

if (this.isPostponed) {
// there was racing resolve/reject which was postponed, to retain valid GCHandle
// in this case we just finish the original resolve/reject
// and we need to use the postponed data/reason
this.isResolved = true;
if (this.reason !== undefined) {
this.complete_task(null, this.reason);
this.complete_task_wrapper(null, this.reason);
} else {
this.complete_task(this.data, null);
this.complete_task_wrapper(this.data, null);
}
} else {
// there is no racing resolve/reject, we can reject/cancel the promise
Expand All @@ -138,11 +153,7 @@ export class PromiseHolder extends ManagedObject {
}

// we can do this just once, because it will be dispose the GCHandle
complete_task(data: any, reason: any) {
if (!loaderHelpers.is_runtime_running()) {
mono_log_debug("This promise can't be propagated to managed code, mono runtime already exited.");
return;
}
complete_task_wrapper(data: any, reason: any) {
try {
mono_assert(!this.isPosted, "Promise is already posted to managed.");
this.isPosted = true;
Expand Down
2 changes: 2 additions & 0 deletions src/mono/browser/runtime/export-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import { getB32, getF32, getF64, getI16, getI32, getI52, getI64Big, getI8, getU1
import { mono_run_main, mono_run_main_and_exit } from "./run";
import { mono_wasm_setenv } from "./startup";
import { loaderHelpers, runtimeHelpers } from "./globals";
import { mono_exit } from "./loader/exit";

export function export_api(): any {
const api: APIType = {
runMain: mono_run_main,
runMainAndExit: mono_run_main_and_exit,
exit: mono_exit,
setEnvironmentVariable: mono_wasm_setenv,
getAssemblyExports: mono_wasm_get_assembly_exports,
setModuleImports: mono_wasm_set_module_imports,
Expand Down
3 changes: 3 additions & 0 deletions src/mono/browser/runtime/gc-handles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ export function setup_managed_proxy(owner: any, gc_handle: GCHandle): void {

export function upgrade_managed_proxy_to_strong_ref(owner: any, gc_handle: GCHandle): void {
const sr = create_strong_ref(owner);
if (_use_finalization_registry) {
_js_owned_object_registry.unregister(owner);
}
_js_owned_object_table.set(gc_handle, sr);
}

Expand Down
2 changes: 2 additions & 0 deletions src/mono/browser/runtime/invoke-js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export function mono_wasm_invoke_jsimport(signature: JSFunctionSignature, args:

export function mono_wasm_invoke_jsimport_ST(function_handle: JSFnHandle, args: JSMarshalerArguments): void {
if (WasmEnableThreads) return;
loaderHelpers.assert_runtime_running();
const bound_fn = js_import_wrapper_by_fn_handle[<any>function_handle];
mono_assert(bound_fn, () => `Imported function handle expected ${function_handle}`);
bound_fn(args);
Expand Down Expand Up @@ -336,6 +337,7 @@ type BindingClosure = {
}

export function mono_wasm_invoke_js_function(bound_function_js_handle: JSHandle, args: JSMarshalerArguments): void {
loaderHelpers.assert_runtime_running();
const bound_fn = mono_wasm_get_jsobj_from_js_handle(bound_function_js_handle);
mono_assert(bound_fn && typeof (bound_fn) === "function" && bound_fn[bound_js_function_symbol], () => `Bound function handle expected ${bound_function_js_handle}`);
bound_fn(args);
Expand Down
2 changes: 2 additions & 0 deletions src/mono/browser/runtime/managed-exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export function call_entry_point(main_assembly_name: string, program_args: strin

// the marshaled signature is: void LoadSatelliteAssembly(byte[] dll)
export function load_satellite_assembly(dll: Uint8Array): void {
loaderHelpers.assert_runtime_running();
const sp = Module.stackSave();
try {
const size = 3;
Expand All @@ -95,6 +96,7 @@ export function load_satellite_assembly(dll: Uint8Array): void {

// the marshaled signature is: void LoadLazyAssembly(byte[] dll, byte[] pdb)
export function load_lazy_assembly(dll: Uint8Array, pdb: Uint8Array | null): void {
loaderHelpers.assert_runtime_running();
const sp = Module.stackSave();
try {
const size = 4;
Expand Down
5 changes: 5 additions & 0 deletions src/mono/browser/runtime/marshal-to-js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { get_marshaler_to_cs_by_type, jsinteropDoc, marshal_exception_to_cs } fr
import { localHeapViewF64, localHeapViewI32, localHeapViewU8 } from "./memory";
import { call_delegate } from "./managed-exports";
import { gc_locked } from "./gc-lock";
import { mono_log_debug } from "./logging";

export function initialize_marshalers_to_js(): void {
if (cs_to_js_marshalers.size == 0) {
Expand Down Expand Up @@ -337,6 +338,10 @@ function create_task_holder(res_converter?: MarshalerToJs) {
}

export function mono_wasm_resolve_or_reject_promise(args: JSMarshalerArguments): void {
if (!loaderHelpers.is_runtime_running()) {
mono_log_debug("This promise resolution/rejection can't be propagated to managed code, mono runtime already exited.");
return;
}
const exc = get_arg(args, 0);
const receiver_should_free = WasmEnableThreads && is_receiver_should_free(args);
try {
Expand Down
7 changes: 7 additions & 0 deletions src/mono/browser/runtime/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,13 @@ export type APIType = {
* @returns exit code of the Main() method.
*/
runMainAndExit: (mainAssemblyName?: string, args?: string[]) => Promise<number>;
/**
* Exits the runtime.
* Note: after the runtime exits, it would reject all further calls to the API.
* @param code "process" exit code.
* @param reason could be a string or an Error object.
*/
exit: (code: number, reason?: any) => void;
/**
* Sets the environment variable for the "process"
* @param name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
{
await DisposeHubConnection();
// exit the client
await JSRuntime.InvokeVoidAsync("eval", "import('./dotnet.js').then(module => { module.dotnet; module.exit(0); });");
await JSRuntime.InvokeVoidAsync("eval", "getDotnetRuntime(0).exit(0);");
}

private async Task DisposeHubConnection()
Expand Down

0 comments on commit ee24988

Please sign in to comment.