Skip to content

Commit

Permalink
[browser][MT] smaller thread pool (dotnet#100415)
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelsavara authored and matouskozak committed Apr 30, 2024
1 parent 8d60b05 commit 8a871fd
Show file tree
Hide file tree
Showing 10 changed files with 19 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<JSType.DiscardNoWait>] // this means that the message will arrive out of order, especially across threads.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,30 +76,7 @@ public JSWebWorkerInstance(Func<Task<T>> body, CancellationToken cancellationTok

public Task<T> 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<T>)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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
</PropertyGroup>
<PropertyGroup Condition="'$(TargetOS)' == 'browser'">
<XunitShowProgress>true</XunitShowProgress>
<_WasmPThreadPoolUnusedSize>10</_WasmPThreadPoolUnusedSize>
</PropertyGroup>
<ItemGroup>
<Compile Include="Helpers.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
<IncludeRemoteExecutor>true</IncludeRemoteExecutor>
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetOS)' == 'browser'">
<XunitShowProgress>true</XunitShowProgress>
<_WasmPThreadPoolUnusedSize>10</_WasmPThreadPoolUnusedSize>
</PropertyGroup>
<ItemGroup>
<Compile Include="CompressedStackTests.cs" />
<Compile Include="ExceptionTests.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
<TestRuntime>true</TestRuntime>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetOS)' == 'browser'">
<XunitShowProgress>true</XunitShowProgress>
<_WasmPThreadPoolUnusedSize>10</_WasmPThreadPoolUnusedSize>
</PropertyGroup>
<ItemGroup>
<Compile Include="ThreadPoolTests.cs" />
<Compile Include="RegisteredWaitTests.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</PropertyGroup>
<PropertyGroup Condition="'$(TargetOS)' == 'browser'">
<XunitShowProgress>true</XunitShowProgress>
<_WasmPThreadPoolUnusedSize>10</_WasmPThreadPoolUnusedSize>
</PropertyGroup>
<ItemGroup>
<Compile Include="AsyncLocalTests.cs" />
Expand Down
3 changes: 1 addition & 2 deletions src/mono/browser/runtime/exports-internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/mono/browser/runtime/loader/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/mono/browser/runtime/pthreads/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
25 changes: 4 additions & 21 deletions src/mono/browser/runtime/pthreads/ui-thread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<PThreadPtr, PromiseController<Thread>[]> = new Map();
Expand Down Expand Up @@ -119,32 +118,16 @@ function monoWorkerMessageHandler (worker: PThreadWorker, ev: MessageEvent<any>)
}
}

let pendingWorkerLoad: PromiseAndController<void> | 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<Worker>): void {
if (!WasmEnableThreads) return;
worker.addEventListener("message", (ev) => monoWorkerMessageHandler(worker, ev));
if (pendingWorkerLoad == undefined) {
pendingWorkerLoad = createPromiseController<void>();
}
loaded.then(() => {
worker.info.isLoaded = true;
if (pendingWorkerLoad != undefined) {
pendingWorkerLoad.promise_control.resolve();
pendingWorkerLoad = undefined;
}
});
}

export function thread_available (): Promise<void> {
if (!WasmEnableThreads) return null as any;
if (pendingWorkerLoad == undefined) {
return Promise.resolve();
}
return pendingWorkerLoad.promise;
}

export function populateEmscriptenPool (): void {
if (!WasmEnableThreads) return;
Expand Down Expand Up @@ -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--;
Expand All @@ -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()!;
}
Expand Down

0 comments on commit 8a871fd

Please sign in to comment.