Skip to content

Commit

Permalink
[wasm] Make runtime_is_initialized promise callbacks one-shot (#70694)
Browse files Browse the repository at this point in the history
* [wasm] Make runtime_is_initialized promise callbacks one-shot

Throw if runtime_is_initialized_resolve or runtime_is_initialized_reject is called more than once

* Add a slightly generalized GuardedPromise<T> object

   Protects against multiple-resolve, multiple-reject, reject after resolve and resolve after reject.

  Does not protect against the executor throwing.
  • Loading branch information
lambdageek authored Jun 14, 2022
1 parent 3428247 commit fb9be0a
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 3 deletions.
34 changes: 34 additions & 0 deletions src/mono/wasm/runtime/guarded-promise.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

/// A Promise<T> that guards against multiple-resolve, multiple-reject, reject-after-accept and accept-after-reject.
class GuardedPromise<T> extends Promise<T> {
constructor(executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void) {
super((resolve, reject) => {
let resolved = false;
let rejected = false;
executor((value: T | PromiseLike<T>) => {
if (resolved) {
throw new Error("Promise resolved more than once");
}
if (rejected) {
throw new Error("Can not resolve a Promise after it has been rejected");
}
resolved = true;
resolve(value);
}, (reason: any) => {
if (resolved) {
throw new Error("Can not reject a Promise after it has been resolved");
}
if (rejected) {
throw new Error("Promise rejected more than once");
}
rejected = true;
reject(reason);
});
});
}
}

export default GuardedPromise;

7 changes: 4 additions & 3 deletions src/mono/wasm/runtime/startup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { AllAssetEntryTypes, mono_assert, AssetEntry, CharPtrNull, DotnetModule,
import { ENVIRONMENT_IS_ESM, ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, INTERNAL, locateFile, Module, MONO, requirePromise, runtimeHelpers } from "./imports";
import cwraps from "./cwraps";
import { mono_wasm_raise_debug_event, mono_wasm_runtime_ready } from "./debug";
import GuardedPromise from "./guarded-promise";
import { mono_wasm_globalization_init, mono_wasm_load_icu_data } from "./icu";
import { toBase64StringImpl } from "./base64";
import { mono_wasm_init_aot_profiler, mono_wasm_init_coverage_profiler } from "./profiler";
Expand All @@ -17,9 +18,9 @@ import { mono_on_abort } from "./run";
import { mono_wasm_new_root } from "./roots";
import { init_crypto } from "./crypto-worker";

export let runtime_is_initialized_resolve: Function;
export let runtime_is_initialized_reject: Function;
export const mono_wasm_runtime_is_initialized = new Promise((resolve, reject) => {
export let runtime_is_initialized_resolve: () => void;
export let runtime_is_initialized_reject: (reason?: any) => void;
export const mono_wasm_runtime_is_initialized = new GuardedPromise<void>((resolve, reject) => {
runtime_is_initialized_resolve = resolve;
runtime_is_initialized_reject = reject;
});
Expand Down

0 comments on commit fb9be0a

Please sign in to comment.