diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs index 2ef8eb48120c9..cede7971db757 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs @@ -517,7 +517,6 @@ private void Dispose(bool disposing) { Environment.FailFast($"JSProxyContext must be disposed on the thread which owns it, ManagedThreadId: {Environment.CurrentManagedThreadId}. {Environment.NewLine} {Environment.StackTrace}"); } - ((GCHandle)ContextHandle).Free(); #endif List> copy = new(ThreadCsOwnedObjects.Values); @@ -531,6 +530,7 @@ private void Dispose(bool disposing) #if FEATURE_WASM_MANAGED_THREADS Interop.Runtime.UninstallWebWorkerInterop(); + ((GCHandle)ContextHandle).Free(); #endif foreach (var gch in ThreadJsOwnedObjects.Values) @@ -544,6 +544,7 @@ private void Dispose(bool disposing) { holder.Callback!.Invoke(null); } + ((GCHandle)holder.GCHandle).Free(); } ThreadCsOwnedObjects.Clear(); diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs index 8d9ea3283c8df..f967540d45079 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs @@ -257,6 +257,10 @@ private void Pump() } try { + if (SynchronizationContext.Current == null) + { + SetSynchronizationContext(this); + } while (Queue.Reader.TryRead(out var item)) { try diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index c3b6c737c9b28..051d97504adf1 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -398,9 +398,6 @@ - - - @@ -616,9 +613,7 @@ - - - + diff --git a/src/mono/browser/runtime/cancelable-promise.ts b/src/mono/browser/runtime/cancelable-promise.ts index 25d7427837948..d55f4052a1a13 100644 --- a/src/mono/browser/runtime/cancelable-promise.ts +++ b/src/mono/browser/runtime/cancelable-promise.ts @@ -78,12 +78,12 @@ 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; } + mono_assert(!this.isResolved, "resolve could be called only once"); + mono_assert(!this.isDisposed, "resolve is already disposed."); if (WasmEnableThreads && !this.setIsResolving()) { // we know that cancelation is in flight // because we need to keep the GCHandle alive until until the cancelation arrives @@ -102,12 +102,12 @@ export class PromiseHolder extends ManagedObject { } 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; } + mono_assert(!this.isResolved, "reject could be called only once"); + mono_assert(!this.isDisposed, "resolve is already disposed."); const isCancelation = reason && reason[promise_holder_symbol] === this; if (WasmEnableThreads && !isCancelation && !this.setIsResolving()) { // we know that cancelation is in flight @@ -127,6 +127,10 @@ export class PromiseHolder extends ManagedObject { } cancel() { + if (!loaderHelpers.is_runtime_running()) { + mono_log_debug("This promise cancelation can't be propagated to managed code, mono runtime already exited."); + return; + } mono_assert(!this.isResolved, "cancel could be called only once"); mono_assert(!this.isDisposed, "resolve is already disposed."); diff --git a/src/mono/browser/runtime/gc-handles.ts b/src/mono/browser/runtime/gc-handles.ts index 61b1640d8993e..cf39ddc822a91 100644 --- a/src/mono/browser/runtime/gc-handles.ts +++ b/src/mono/browser/runtime/gc-handles.ts @@ -160,7 +160,7 @@ export function teardown_managed_proxy(owner: any, gc_handle: GCHandle, skipMana } } if (gc_handle !== GCHandleNull && _js_owned_object_table.delete(gc_handle) && !skipManaged) { - if (loaderHelpers.is_runtime_running()) { + if (loaderHelpers.is_runtime_running() && !force_dispose_proxies_in_progress) { release_js_owned_object_by_gc_handle(gc_handle); } } @@ -204,11 +204,14 @@ export function assertNoProxies(): void { mono_assert(js_import_wrapper_by_fn_handle.length === 1, "There should be no imports on this thread."); } +let force_dispose_proxies_in_progress = false; + // when we arrive here from UninstallWebWorkerInterop, the C# will unregister the handles too. // when called from elsewhere, C# side could be unbalanced!! export function forceDisposeProxies(disposeMethods: boolean, verbose: boolean): void { let keepSomeCsAlive = false; let keepSomeJsAlive = false; + force_dispose_proxies_in_progress = true; let doneImports = 0; let doneExports = 0; diff --git a/src/mono/browser/runtime/http.ts b/src/mono/browser/runtime/http.ts index 9f606f481d395..0e45e2ec81975 100644 --- a/src/mono/browser/runtime/http.ts +++ b/src/mono/browser/runtime/http.ts @@ -25,7 +25,11 @@ function commonAsserts(controller: HttpController) { mono_assert(controller, "expected controller"); } +let http_wasm_supports_streaming_request_cached: boolean | undefined; export function http_wasm_supports_streaming_request(): boolean { + if (http_wasm_supports_streaming_request_cached !== undefined) { + return http_wasm_supports_streaming_request_cached; + } // Detecting streaming request support works like this: // If the browser doesn't support a particular body type, it calls toString() on the object and uses the result as the body. // So, if the browser doesn't support request streams, the request body becomes the string "[object ReadableStream]". @@ -43,13 +47,20 @@ export function http_wasm_supports_streaming_request(): boolean { return "half"; }, } as RequestInit /* https://github.com/microsoft/TypeScript-DOM-lib-generator/issues/1483 */).headers.has("Content-Type"); - return duplexAccessed && !hasContentType; + http_wasm_supports_streaming_request_cached = duplexAccessed && !hasContentType; + } else { + http_wasm_supports_streaming_request_cached = false; } - return false; + return http_wasm_supports_streaming_request_cached; } +let http_wasm_supports_streaming_response_cached: boolean | undefined; export function http_wasm_supports_streaming_response(): boolean { - return typeof Response !== "undefined" && "body" in Response.prototype && typeof ReadableStream === "function"; + if (http_wasm_supports_streaming_response_cached !== undefined) { + return http_wasm_supports_streaming_response_cached; + } + http_wasm_supports_streaming_response_cached = typeof Response !== "undefined" && "body" in Response.prototype && typeof ReadableStream === "function"; + return http_wasm_supports_streaming_response_cached; } export function http_wasm_create_controller(): HttpController { diff --git a/src/mono/browser/runtime/marshal.ts b/src/mono/browser/runtime/marshal.ts index 35e001c8665e0..4c531eec07009 100644 --- a/src/mono/browser/runtime/marshal.ts +++ b/src/mono/browser/runtime/marshal.ts @@ -202,7 +202,7 @@ export function set_arg_element_type(arg: JSMarshalerArgument, type: MarshalerTy export function get_arg_bool(arg: JSMarshalerArgument): boolean { mono_assert(arg, "Null arg"); - return getB32(arg); + return !!getU8(arg); } export function get_arg_u8(arg: JSMarshalerArgument): number {