From 8a871fd9f2b405053dc99aa2f1ea9d3de89c8a6d Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Wed, 3 Apr 2024 09:52:32 +0200 Subject: [PATCH] [browser][MT] smaller thread pool (#100415) --- .../Interop/JavaScriptImports.Generated.cs | 5 ---- .../InteropServices/JavaScript/JSWebWorker.cs | 25 +------------------ .../System.Threading.Tasks.Tests.csproj | 1 + .../System.Threading.Thread.Tests.csproj | 4 +++ .../System.Threading.ThreadPool.Tests.csproj | 4 +++ .../tests/System.Threading.Tests.csproj | 1 + src/mono/browser/runtime/exports-internal.ts | 3 +-- src/mono/browser/runtime/loader/config.ts | 4 +-- src/mono/browser/runtime/pthreads/index.ts | 2 +- .../browser/runtime/pthreads/ui-thread.ts | 25 +++---------------- 10 files changed, 19 insertions(+), 55 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs index 10f737f5a9c9ce..e7ba1f9aadd65c 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs @@ -48,11 +48,6 @@ internal static unsafe partial class JavaScriptImports [JSImport("INTERNAL.mono_wasm_bind_cs_function")] public static partial void BindCSFunction(IntPtr monoMethod, string assemblyName, string namespaceName, string shortClassName, string methodName, int signatureHash, IntPtr signature); -#if FEATURE_WASM_MANAGED_THREADS - [JSImport("INTERNAL.thread_available")] - public static partial Task ThreadAvailable(); -#endif - #if DEBUG [JSImport("globalThis.console.log")] [return: JSMarshalAs] // this means that the message will arrive out of order, especially across threads. diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSWebWorker.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSWebWorker.cs index 928339b8062061..776f6dc3d5dbe2 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSWebWorker.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSWebWorker.cs @@ -76,30 +76,7 @@ public JSWebWorkerInstance(Func> body, CancellationToken cancellationTok public Task Start() { - if (JSProxyContext.MainThreadContext.IsCurrentThread()) - { - // give browser chance to load more threads - // until there at least one thread loaded, it doesn't make sense to `Start` - // because that would also hang, but in a way blocking the UI thread, much worse. - JavaScriptImports.ThreadAvailable().ContinueWith(static (t, o) => - { - var self = (JSWebWorkerInstance)o!; - if (t.IsCompletedSuccessfully) - { - self._thread.Start(); - } - if (t.IsCanceled) - { - throw new OperationCanceledException("Cancelled while waiting for underlying WebWorker to become available.", self._cancellationToken); - } - throw t.Exception!; - // ideally this will execute on UI thread quickly: ExecuteSynchronously - }, this, _cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.FromCurrentSynchronizationContext()); - } - else - { - _thread.Start(); - } + _thread.Start(); return _taskCompletionSource.Task; } diff --git a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Threading.Tasks.Tests.csproj b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Threading.Tasks.Tests.csproj index 8f7a8cb6b51cf3..57ce44b8d41a12 100644 --- a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Threading.Tasks.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Threading.Tasks.Tests.csproj @@ -6,6 +6,7 @@ true + <_WasmPThreadPoolUnusedSize>10 diff --git a/src/libraries/System.Threading.Thread/tests/System.Threading.Thread.Tests.csproj b/src/libraries/System.Threading.Thread/tests/System.Threading.Thread.Tests.csproj index 6a9977dc6e11b6..ed29b66576eea4 100644 --- a/src/libraries/System.Threading.Thread/tests/System.Threading.Thread.Tests.csproj +++ b/src/libraries/System.Threading.Thread/tests/System.Threading.Thread.Tests.csproj @@ -5,6 +5,10 @@ true $(NetCoreAppCurrent) + + true + <_WasmPThreadPoolUnusedSize>10 + diff --git a/src/libraries/System.Threading.ThreadPool/tests/System.Threading.ThreadPool.Tests.csproj b/src/libraries/System.Threading.ThreadPool/tests/System.Threading.ThreadPool.Tests.csproj index 0cb21c9d38492b..ad3f9814e0adfc 100644 --- a/src/libraries/System.Threading.ThreadPool/tests/System.Threading.ThreadPool.Tests.csproj +++ b/src/libraries/System.Threading.ThreadPool/tests/System.Threading.ThreadPool.Tests.csproj @@ -4,6 +4,10 @@ $(NetCoreAppCurrent) true + + true + <_WasmPThreadPoolUnusedSize>10 + diff --git a/src/libraries/System.Threading/tests/System.Threading.Tests.csproj b/src/libraries/System.Threading/tests/System.Threading.Tests.csproj index 54261d3a1fe665..768bb7b665d925 100644 --- a/src/libraries/System.Threading/tests/System.Threading.Tests.csproj +++ b/src/libraries/System.Threading/tests/System.Threading.Tests.csproj @@ -9,6 +9,7 @@ true + <_WasmPThreadPoolUnusedSize>10 diff --git a/src/mono/browser/runtime/exports-internal.ts b/src/mono/browser/runtime/exports-internal.ts index 074b54a18fcc7e..c2ef78c0a1f9e3 100644 --- a/src/mono/browser/runtime/exports-internal.ts +++ b/src/mono/browser/runtime/exports-internal.ts @@ -23,7 +23,7 @@ import { mono_wasm_get_func_id_to_name_mappings } from "./logging"; import { monoStringToStringUnsafe } from "./strings"; import { mono_wasm_bind_cs_function } from "./invoke-cs"; -import { mono_wasm_dump_threads, thread_available } from "./pthreads"; +import { mono_wasm_dump_threads } from "./pthreads"; export function export_internal (): any { return { @@ -63,7 +63,6 @@ export function export_internal (): any { get_global_this, get_dotnet_instance: () => exportedRuntimeAPI, dynamic_import, - thread_available: WasmEnableThreads ? thread_available : undefined, mono_wasm_bind_cs_function, // BrowserWebSocket diff --git a/src/mono/browser/runtime/loader/config.ts b/src/mono/browser/runtime/loader/config.ts index 1739bc09d1ef67..8fd7f00fe3152d 100644 --- a/src/mono/browser/runtime/loader/config.ts +++ b/src/mono/browser/runtime/loader/config.ts @@ -190,10 +190,10 @@ export function normalizeConfig () { if (WasmEnableThreads) { if (!Number.isInteger(config.pthreadPoolInitialSize)) { - config.pthreadPoolInitialSize = 7; + config.pthreadPoolInitialSize = 5; } if (!Number.isInteger(config.pthreadPoolUnusedSize)) { - config.pthreadPoolUnusedSize = 3; + config.pthreadPoolUnusedSize = 1; } if (!Number.isInteger(config.finalizerThreadStartDelayMs)) { config.finalizerThreadStartDelayMs = 200; diff --git a/src/mono/browser/runtime/pthreads/index.ts b/src/mono/browser/runtime/pthreads/index.ts index 0a5911605282d0..195df3e126ab65 100644 --- a/src/mono/browser/runtime/pthreads/index.ts +++ b/src/mono/browser/runtime/pthreads/index.ts @@ -6,7 +6,7 @@ export { mono_wasm_pthread_ptr, update_thread_info, isMonoThreadMessage, monoThreadInfo, } from "./shared"; export { - mono_wasm_dump_threads, thread_available, cancelThreads, is_thread_available, + mono_wasm_dump_threads, cancelThreads, is_thread_available, populateEmscriptenPool, mono_wasm_init_threads, init_finalizer_thread, waitForThread, replaceEmscriptenPThreadUI } from "./ui-thread"; diff --git a/src/mono/browser/runtime/pthreads/ui-thread.ts b/src/mono/browser/runtime/pthreads/ui-thread.ts index fcb010ae3a187c..14a7c9353b2fb9 100644 --- a/src/mono/browser/runtime/pthreads/ui-thread.ts +++ b/src/mono/browser/runtime/pthreads/ui-thread.ts @@ -5,11 +5,10 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import BuildConfiguration from "consts:configuration"; import { } from "../globals"; -import { mono_log_debug, mono_log_warn } from "../logging"; import { MonoWorkerToMainMessage, monoThreadInfo, mono_wasm_pthread_ptr, update_thread_info, worker_empty_prefix } from "./shared"; import { Module, ENVIRONMENT_IS_WORKER, createPromiseController, loaderHelpers, mono_assert, runtimeHelpers } from "../globals"; -import { PThreadLibrary, MainToWorkerMessageType, MonoThreadMessage, PThreadInfo, PThreadPtr, PThreadPtrNull, PThreadWorker, PromiseAndController, PromiseController, Thread, WorkerToMainMessageType, monoMessageSymbol } from "../types/internal"; -import { mono_log_error, mono_log_info } from "../logging"; +import { PThreadLibrary, MainToWorkerMessageType, MonoThreadMessage, PThreadInfo, PThreadPtr, PThreadPtrNull, PThreadWorker, PromiseController, Thread, WorkerToMainMessageType, monoMessageSymbol } from "../types/internal"; +import { mono_log_error, mono_log_info, mono_log_debug } from "../logging"; import { threads_c_functions as cwraps } from "../cwraps"; const threadPromises: Map[]> = new Map(); @@ -119,32 +118,16 @@ function monoWorkerMessageHandler (worker: PThreadWorker, ev: MessageEvent) } } -let pendingWorkerLoad: PromiseAndController | undefined; - /// Called by Emscripten internals on the browser thread when a new pthread worker is created and added to the pthread worker pool. /// At this point the worker doesn't have any pthread assigned to it, yet. export function onWorkerLoadInitiated (worker: PThreadWorker, loaded: Promise): void { if (!WasmEnableThreads) return; worker.addEventListener("message", (ev) => monoWorkerMessageHandler(worker, ev)); - if (pendingWorkerLoad == undefined) { - pendingWorkerLoad = createPromiseController(); - } loaded.then(() => { worker.info.isLoaded = true; - if (pendingWorkerLoad != undefined) { - pendingWorkerLoad.promise_control.resolve(); - pendingWorkerLoad = undefined; - } }); } -export function thread_available (): Promise { - if (!WasmEnableThreads) return null as any; - if (pendingWorkerLoad == undefined) { - return Promise.resolve(); - } - return pendingWorkerLoad.promise; -} export function populateEmscriptenPool (): void { if (!WasmEnableThreads) return; @@ -295,7 +278,7 @@ function getNewWorker (modulePThread: PThreadLibrary): PThreadWorker { if (!WasmEnableThreads) return null as any; if (modulePThread.unusedWorkers.length == 0) { - mono_log_warn(`Failed to find unused WebWorker, this may deadlock. Please increase the pthreadPoolReady. Running threads ${modulePThread.runningWorkers.length}. Loading workers: ${modulePThread.unusedWorkers.length}`); + mono_log_debug(`Failed to find unused WebWorker, this may deadlock. Please increase the pthreadPoolReady. Running threads ${modulePThread.runningWorkers.length}. Loading workers: ${modulePThread.unusedWorkers.length}`); const worker = allocateUnusedWorker(); modulePThread.loadWasmModuleToWorker(worker); availableThreadCount--; @@ -316,7 +299,7 @@ function getNewWorker (modulePThread: PThreadLibrary): PThreadWorker { return worker; } } - mono_log_warn(`Failed to find loaded WebWorker, this may deadlock. Please increase the pthreadPoolReady. Running threads ${modulePThread.runningWorkers.length}. Loading workers: ${modulePThread.unusedWorkers.length}`); + mono_log_debug(`Failed to find loaded WebWorker, this may deadlock. Please increase the pthreadPoolReady. Running threads ${modulePThread.runningWorkers.length}. Loading workers: ${modulePThread.unusedWorkers.length}`); availableThreadCount--; // negative value return modulePThread.unusedWorkers.pop()!; }