Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[browser][MT] smaller thread pool #100415

Merged
merged 4 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading