-mono_crash*
\ No newline at end of file
+mono_crash*
+wasi-sdk
\ No newline at end of file
return 0;
}
+ [JSImport("globalThis.console.log")]
+ public static partial void ConsoleLog(string status);
+
[JSImport("Sample.Test.updateProgress", "main.js")]
static partial void updateProgress(string status);
public void Run()
{
+ Sample.Test.ConsoleLog("Hello from ManagedThreadId " + Thread.CurrentThread.ManagedThreadId);
long result = Fib(UpTo);
if (result < (long)int.MaxValue)
_tcs.SetResult((int)result);
<Import Project="..\DefaultBrowserSample.targets" />
<PropertyGroup>
<_SampleProject>Wasm.Browser.Threads.Sample.csproj</_SampleProject>
+ <WasmEnableThreads>true</WasmEnableThreads>
</PropertyGroup>
- <Target Name="CheckThreadsEnabled" BeforeTargets="Compile" >
- <Warning Condition="'$(WasmEnableThreads)' != 'true'" Text="This sample requires threading" />
- </Target>
-
<!-- set the condition to false and you will get a CA1416 error about the call to Thread.Start from a browser-wasm project -->
<ItemGroup Condition="true">
<!-- TODO: some .props file that automates this. Unfortunately just adding a ProjectReference to Microsoft.NET.WebAssembly.Threading.proj doesn't work - it ends up bundling the ref assemblies into the publish directory and breaking the app. -->
pendingAsset: AssetEntryInternal,
wasmModuleImports: WebAssembly.Imports,
successCallback: InstantiateWasmSuccessCallback,
-) {
+): Promise<void> {
mono_assert(pendingAsset && pendingAsset.pendingDownloadInternal, "Can't load dotnet.wasm");
const response = await pendingAsset.pendingDownloadInternal.response;
const contentType = response.headers ? response.headers.get("Content-Type") : undefined;
MonoMethod, MonoObject, MonoString,
MonoType, MonoObjectRef, MonoStringRef, JSMarshalerArguments
} from "./types";
-import { ENVIRONMENT_IS_PTHREAD, Module } from "./imports";
+import { Module } from "./imports";
import { VoidPtr, CharPtrPtr, Int32Ptr, CharPtr, ManagedPointer } from "./types/emscripten";
type SigLine = [lazy: boolean, name: string, returnType: string | null, argTypes?: string[], opts?: any];
mono_jiterp_get_offset_of_vtable_initialized_flag(): number;
mono_jiterp_get_offset_of_array_data(): number;
// Returns bytes written (or 0 if writing failed)
- mono_jiterp_encode_leb52 (destination: VoidPtr, value: number, valueIsSigned: number): number;
+ mono_jiterp_encode_leb52(destination: VoidPtr, value: number, valueIsSigned: number): number;
// Returns bytes written (or 0 if writing failed)
// Source is the address of a 64-bit int or uint
- mono_jiterp_encode_leb64_ref (destination: VoidPtr, source: VoidPtr, valueIsSigned: number): number;
- mono_jiterp_type_is_byref (type: MonoType): number;
- mono_jiterp_get_size_of_stackval (): number;
- mono_jiterp_type_get_raw_value_size (type: MonoType): number;
- mono_jiterp_parse_option (name: string): number;
- mono_jiterp_get_options_as_json (): number;
- mono_jiterp_get_options_version (): number;
- mono_jiterp_adjust_abort_count (opcode: number, delta: number): number;
- mono_jiterp_register_jit_call_thunk (cinfo: number, func: number): void;
- mono_jiterp_update_jit_call_dispatcher (fn: number): void;
+ mono_jiterp_encode_leb64_ref(destination: VoidPtr, source: VoidPtr, valueIsSigned: number): number;
+ mono_jiterp_type_is_byref(type: MonoType): number;
+ mono_jiterp_get_size_of_stackval(): number;
+ mono_jiterp_type_get_raw_value_size(type: MonoType): number;
+ mono_jiterp_parse_option(name: string): number;
+ mono_jiterp_get_options_as_json(): number;
+ mono_jiterp_get_options_version(): number;
+ mono_jiterp_adjust_abort_count(opcode: number, delta: number): number;
+ mono_jiterp_register_jit_call_thunk(cinfo: number, func: number): void;
+ mono_jiterp_update_jit_call_dispatcher(fn: number): void;
}
const wrapped_c_functions: t_Cwraps = <any>{};
}
export function init_c_exports(): void {
- // init_c_exports is called very early in a pthread before Module.cwrap is available
- const alwaysLazy = !!ENVIRONMENT_IS_PTHREAD;
for (const sig of fn_signatures) {
const wf: any = wrapped_c_functions;
const [lazy, name, returnType, argTypes, opts] = sig;
- if (lazy || alwaysLazy) {
+ if (lazy) {
// lazy init on first run
wf[name] = function (...args: any[]) {
const fce = Module.cwrap(name, returnType, argTypes, opts);
list.registerRuntime(exportedRuntimeAPI);
if (MonoWasmThreads && ENVIRONMENT_IS_PTHREAD) {
- // eslint-disable-next-line no-inner-declarations
- async function workerInit(): Promise<DotnetModule> {
- await mono_wasm_pthread_worker_init();
-
- // HACK: Emscripten's dotnet.worker.js expects the exports of dotnet.js module to be Module object
- // until we have our own fix for dotnet.worker.js file
- // we also skip all emscripten startup event and configuration of worker's JS state
- // note that emscripten events are not firing either
-
- return exportedRuntimeAPI.Module;
- }
- // Emscripten pthread worker.js is ok with a Promise here.
- return <any>workerInit();
+ return <any>mono_wasm_pthread_worker_init(module, exportedRuntimeAPI);
}
configure_emscripten_startup(module, exportedRuntimeAPI);
emscriptenStartup = "mono.emscriptenStartup",
instantiateWasm = "mono.instantiateWasm",
preInit = "mono.preInit",
+ preInitWorker = "mono.preInitWorker",
preRun = "mono.preRun",
+ preRunWorker = "mono.preRunWorker",
onRuntimeInitialized = "mono.onRuntimeInitialized",
postRun = "mono.postRun",
loadRuntime = "mono.loadRuntime",
/// <reference lib="webworker" />
import MonoWasmThreads from "consts:monoWasmThreads";
-import { Module, ENVIRONMENT_IS_PTHREAD } from "../../imports";
+import { Module, ENVIRONMENT_IS_PTHREAD, runtimeHelpers } from "../../imports";
import { isMonoThreadMessageApplyMonoConfig, makeChannelCreatedMonoMessage } from "../shared";
import type { pthread_ptr } from "../shared/types";
import { mono_assert, is_nullish, MonoConfig } from "../../types";
WorkerThreadEventTarget
} from "./events";
import { setup_proxy_console } from "../../logging";
+import { afterConfigLoaded, preRunWorker } from "../../startup";
// re-export some of the events types
export {
return pthread_self;
}
-// TODO: should we just assign to Module.config here?
-let workerMonoConfig: MonoConfig = null as unknown as MonoConfig;
+let workerMonoConfigReceived = false;
// called when the main thread sends us the mono config
function onMonoConfigReceived(config: MonoConfig): void {
- if (workerMonoConfig !== null) {
+ if (workerMonoConfigReceived) {
console.debug("MONO_WASM: mono config already received");
return;
}
- workerMonoConfig = config;
- console.debug("MONO_WASM: mono config received", config);
- if (workerMonoConfig.diagnosticTracing) {
+
+ console.debug("MONO_WASM: mono config received");
+ config = runtimeHelpers.config = Module.config = Object.assign(Module.config || {} as any, config);
+ workerMonoConfigReceived = true;
+
+ afterConfigLoaded.promise_control.resolve(config);
+
+ if (config.diagnosticTracing) {
setup_proxy_console("pthread-worker", console, self.location.href);
}
}
const self = pthread_self;
mono_assert(self !== null && self.pthread_id == pthread_id, "expected pthread_self to be set already when attaching");
console.debug("MONO_WASM: attaching pthread to runtime", pthread_id);
+ preRunWorker();
currentWorkerThreadEvents.dispatchEvent(makeWorkerThreadEvent(dotnetPthreadAttached, self));
}
let configLoaded = false;
let isCustomStartup = false;
export const dotnetReady = createPromiseController<any>();
-export const afterConfigLoaded = createPromiseController<void>();
+export const afterConfigLoaded = createPromiseController<MonoConfig>();
export const afterInstantiateWasm = createPromiseController<void>();
export const beforePreInit = createPromiseController<void>();
export const afterPreInit = createPromiseController<void>();
})();
}
+async function preInitWorkerAsync() {
+ const mark = startMeasure();
+ try {
+ if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: preInitWorker");
+ beforePreInit.promise_control.resolve();
+ mono_wasm_pre_init_essential();
+ await init_polyfills_async();
+ afterPreInit.promise_control.resolve();
+ endMeasure(mark, MeasuredBlock.preInitWorker);
+ } catch (err) {
+ _print_error("MONO_WASM: user preInitWorker() failed", err);
+ abort_startup(err, true);
+ throw err;
+ }
+}
+
+export function preRunWorker() {
+ const mark = startMeasure();
+ try {
+ bindings_init();
+ endMeasure(mark, MeasuredBlock.preRunWorker);
+ } catch (err) {
+ abort_startup(err, true);
+ throw err;
+ }
+ // signal next stage
+ afterPreRun.promise_control.resolve();
+}
+
async function preRunAsync(userPreRun: (() => void)[]) {
Module.addRunDependency("mono_pre_run_async");
// wait for previous stages
await start_asset_download_with_retries(assetToLoad, false);
await beforePreInit.promise;
Module.addRunDependency("instantiate_wasm_module");
- instantiate_wasm_asset(assetToLoad, imports, successCallback);
+ await instantiate_wasm_asset(assetToLoad, imports, successCallback);
if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: instantiate_wasm_module done");
afterInstantiateWasm.promise_control.resolve();
configLoaded = true;
if (!configFilePath) {
normalize();
- afterConfigLoaded.promise_control.resolve();
+ afterConfigLoaded.promise_control.resolve(runtimeHelpers.config);
return;
}
if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: mono_wasm_load_config");
throw err;
}
}
- afterConfigLoaded.promise_control.resolve();
+ afterConfigLoaded.promise_control.resolve(runtimeHelpers.config);
} catch (err) {
const errMessage = `Failed to load config file ${configFilePath} ${err}`;
abort_startup(errMessage, true);
}
/// Called when dotnet.worker.js receives an emscripten "load" event from the main thread.
+/// This method is comparable to configure_emscripten_startup function
///
/// Notes:
/// 1. Emscripten skips a lot of initialization on the pthread workers, Module may not have everything you expect.
-/// 2. Emscripten does not run the preInit or preRun functions in the workers.
+/// 2. Emscripten does not run any event but preInit in the workers.
/// 3. At the point when this executes there is no pthread assigned to the worker yet.
-export async function mono_wasm_pthread_worker_init(): Promise<void> {
+export async function mono_wasm_pthread_worker_init(module: DotnetModule, exportedAPI: RuntimeAPI): Promise<DotnetModule> {
console.debug("MONO_WASM: worker initializing essential C exports and APIs");
- // FIXME: copy/pasted from mono_wasm_pre_init_essential - can we share this code? Any other global state that needs initialization?
- init_c_exports();
- // not initializing INTERNAL, MONO, or BINDING C wrappers here - those legacy APIs are not likely to be needed on pthread workers.
// This is a good place for subsystems to attach listeners for pthreads_worker.currentWorkerThreadEvents
pthreads_worker.currentWorkerThreadEvents.addEventListener(pthreads_worker.dotnetPthreadCreated, (ev) => {
console.debug("MONO_WASM: pthread created", ev.pthread_self.pthread_id);
});
+ // this is the only event which is called on worker
+ module.preInit = [() => preInitWorkerAsync()];
+
+ await afterPreInit.promise;
+ return exportedAPI.Module;
}
/**