Skip to content

Commit ee24988

Browse files
committed
- more testing if the runtime is still running
- 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
1 parent b0f6444 commit ee24988

File tree

8 files changed

+42
-10
lines changed

8 files changed

+42
-10
lines changed

src/mono/browser/runtime/cancelable-promise.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ export function wrap_as_cancelable<T>(inner: Promise<T>): ControllablePromise<T>
3636
}
3737

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

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

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

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

118133
if (this.isPostponed) {
119134
// there was racing resolve/reject which was postponed, to retain valid GCHandle
120135
// in this case we just finish the original resolve/reject
121136
// and we need to use the postponed data/reason
122137
this.isResolved = true;
123138
if (this.reason !== undefined) {
124-
this.complete_task(null, this.reason);
139+
this.complete_task_wrapper(null, this.reason);
125140
} else {
126-
this.complete_task(this.data, null);
141+
this.complete_task_wrapper(this.data, null);
127142
}
128143
} else {
129144
// there is no racing resolve/reject, we can reject/cancel the promise
@@ -138,11 +153,7 @@ export class PromiseHolder extends ManagedObject {
138153
}
139154

140155
// we can do this just once, because it will be dispose the GCHandle
141-
complete_task(data: any, reason: any) {
142-
if (!loaderHelpers.is_runtime_running()) {
143-
mono_log_debug("This promise can't be propagated to managed code, mono runtime already exited.");
144-
return;
145-
}
156+
complete_task_wrapper(data: any, reason: any) {
146157
try {
147158
mono_assert(!this.isPosted, "Promise is already posted to managed.");
148159
this.isPosted = true;

src/mono/browser/runtime/export-api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ import { getB32, getF32, getF64, getI16, getI32, getI52, getI64Big, getI8, getU1
99
import { mono_run_main, mono_run_main_and_exit } from "./run";
1010
import { mono_wasm_setenv } from "./startup";
1111
import { loaderHelpers, runtimeHelpers } from "./globals";
12+
import { mono_exit } from "./loader/exit";
1213

1314
export function export_api(): any {
1415
const api: APIType = {
1516
runMain: mono_run_main,
1617
runMainAndExit: mono_run_main_and_exit,
18+
exit: mono_exit,
1719
setEnvironmentVariable: mono_wasm_setenv,
1820
getAssemblyExports: mono_wasm_get_assembly_exports,
1921
setModuleImports: mono_wasm_set_module_imports,

src/mono/browser/runtime/gc-handles.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ export function setup_managed_proxy(owner: any, gc_handle: GCHandle): void {
139139

140140
export function upgrade_managed_proxy_to_strong_ref(owner: any, gc_handle: GCHandle): void {
141141
const sr = create_strong_ref(owner);
142+
if (_use_finalization_registry) {
143+
_js_owned_object_registry.unregister(owner);
144+
}
142145
_js_owned_object_table.set(gc_handle, sr);
143146
}
144147

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export function mono_wasm_invoke_jsimport(signature: JSFunctionSignature, args:
5353

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

338339
export function mono_wasm_invoke_js_function(bound_function_js_handle: JSHandle, args: JSMarshalerArguments): void {
340+
loaderHelpers.assert_runtime_running();
339341
const bound_fn = mono_wasm_get_jsobj_from_js_handle(bound_function_js_handle);
340342
mono_assert(bound_fn && typeof (bound_fn) === "function" && bound_fn[bound_js_function_symbol], () => `Bound function handle expected ${bound_function_js_handle}`);
341343
bound_fn(args);

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export function call_entry_point(main_assembly_name: string, program_args: strin
8080

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

9697
// the marshaled signature is: void LoadLazyAssembly(byte[] dll, byte[] pdb)
9798
export function load_lazy_assembly(dll: Uint8Array, pdb: Uint8Array | null): void {
99+
loaderHelpers.assert_runtime_running();
98100
const sp = Module.stackSave();
99101
try {
100102
const size = 4;

src/mono/browser/runtime/marshal-to-js.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { get_marshaler_to_cs_by_type, jsinteropDoc, marshal_exception_to_cs } fr
2323
import { localHeapViewF64, localHeapViewI32, localHeapViewU8 } from "./memory";
2424
import { call_delegate } from "./managed-exports";
2525
import { gc_locked } from "./gc-lock";
26+
import { mono_log_debug } from "./logging";
2627

2728
export function initialize_marshalers_to_js(): void {
2829
if (cs_to_js_marshalers.size == 0) {
@@ -337,6 +338,10 @@ function create_task_holder(res_converter?: MarshalerToJs) {
337338
}
338339

339340
export function mono_wasm_resolve_or_reject_promise(args: JSMarshalerArguments): void {
341+
if (!loaderHelpers.is_runtime_running()) {
342+
mono_log_debug("This promise resolution/rejection can't be propagated to managed code, mono runtime already exited.");
343+
return;
344+
}
340345
const exc = get_arg(args, 0);
341346
const receiver_should_free = WasmEnableThreads && is_receiver_should_free(args);
342347
try {

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,13 @@ export type APIType = {
415415
* @returns exit code of the Main() method.
416416
*/
417417
runMainAndExit: (mainAssemblyName?: string, args?: string[]) => Promise<number>;
418+
/**
419+
* Exits the runtime.
420+
* Note: after the runtime exits, it would reject all further calls to the API.
421+
* @param code "process" exit code.
422+
* @param reason could be a string or an Error object.
423+
*/
424+
exit: (code: number, reason?: any) => void;
418425
/**
419426
* Sets the environment variable for the "process"
420427
* @param name

src/mono/wasm/testassets/BlazorHostedApp/BlazorHosted.Client/Pages/Chat.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
{
8787
await DisposeHubConnection();
8888
// exit the client
89-
await JSRuntime.InvokeVoidAsync("eval", "import('./dotnet.js').then(module => { module.dotnet; module.exit(0); });");
89+
await JSRuntime.InvokeVoidAsync("eval", "getDotnetRuntime(0).exit(0);");
9090
}
9191

9292
private async Task DisposeHubConnection()

0 commit comments

Comments
 (0)