diff --git a/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml b/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml index ed31b829c0ce68..e6be56a9307d60 100644 --- a/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml +++ b/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml @@ -262,7 +262,6 @@ jobs: isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }} isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }} runOnlyOnWasmOnlyPipelines: true - shouldContinueOnError: true # Disable for now #- template: /eng/pipelines/coreclr/perf-wasm-jobs.yml diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/AssignmentTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/AssignmentTests.cs index 76b75c452e4d8d..ea2fc12f67e084 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/AssignmentTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/AssignmentTests.cs @@ -50,7 +50,6 @@ public AssignmentTests(ITestOutputHelper testOutput) [ConditionalTheory(nameof(RunningOnChrome))] [MemberData("GetTestData")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))] public async Task InspectVariableBeforeAndAfterAssignment(string clazz, JObject checkDefault, JObject checkValue, string methodName) { await SetBreakpointInMethod("debugger-test", "DebuggerTests." + clazz, "Prepare", 2); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs index 356f7bb4c1f4a5..3294ed1fcd92d5 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs @@ -217,7 +217,6 @@ await EvaluateAndCheck( [MemberData(nameof(FalseConditions))] [MemberData(nameof(TrueConditions))] [MemberData(nameof(InvalidConditions))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))] public async Task ConditionalBreakpoint2(string function_to_call, string method_to_stop, string condition, bool bp_stop_expected) { Result [] bps = new Result[2]; @@ -700,7 +699,6 @@ public async Task StepThroughOrNonUserCodeAttributeResumeWithBp(bool justMyCodeE [InlineData(true, "Debugger.stepInto", "RunStepThroughWithNonUserCode", "RunStepThroughWithNonUserCode", -1, 8, "RunStepThroughWithNonUserCode", -1, 4)] [InlineData(false, "Debugger.resume", "RunStepThroughWithNonUserCode", "StepThroughWithNonUserCodeUserBp", 927, 8, "RunStepThroughWithNonUserCode", -1, 4)] [InlineData(true, "Debugger.resume", "RunStepThroughWithNonUserCode", "RunStepThroughWithNonUserCode", -1, 8, "RunStepThroughWithNonUserCode", -1, 4)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))] public async Task StepThroughOrNonUserCodeAttributeWithUserBp( bool justMyCodeEnabled, string debuggingFunction, string evalFunName, string functionNameCheck1, int line1, int col1, diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs index f03f0518e00205..1e87f9e60cc0bf 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs @@ -260,7 +260,6 @@ public GetPropertiesTests(ITestOutputHelper testOutput) : base(testOutput) [MemberData(nameof(ClassGetPropertiesTestData), parameters: false)] [MemberData(nameof(StructGetPropertiesTestData), parameters: true)] [MemberData(nameof(StructGetPropertiesTestData), parameters: false)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))] public async Task InspectTypeInheritedMembers(string type_name, bool? own_properties, bool? accessors_only, string[] expected_names, Dictionary all_props, bool is_async) => await InspectTypeInheritedMembersInternal(type_name, own_properties, accessors_only, expected_names, all_props, is_async, AutoEvaluate.Unset); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs index 6f391cf5b6eb5e..80345ecb16add5 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs @@ -1163,9 +1163,19 @@ await StepAndCheck(StepKind.Resume, "dotnet://debugger-test.dll/debugger-test.cs ); } - [ConditionalFact(nameof(WasmMultiThreaded))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))] - public async Task TestDebugUsingMultiThreadedRuntime() + public static TheoryData CountToTen() + { + var data = new TheoryData(); + for(int i=0;i<10;i++) + { + data.Add(i); + } + return data; + } + + [ConditionalTheory(nameof(WasmMultiThreaded))] + [MemberData(nameof(CountToTen))] + public async Task TestDebugUsingMultiThreadedRuntime(int _attempt) { var bp = await SetBreakpointInMethod("debugger-test.dll", "MultiThreadedTest", "Write", 2); var expression = $"{{ invoke_static_method('[debugger-test] MultiThreadedTest:Run'); }}"; diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs index b98a56060ea0c6..b97122033f96bf 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs @@ -1222,7 +1222,6 @@ await StepAndCheck(StepKind.Into, "dotnet://Newtonsoft.Json.dll/JArray.cs", 350, [ConditionalTheory(nameof(RunningOnChrome))] [InlineData(true)] [InlineData(false)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))] public async Task SkipWasmFunctionsAccordinglyJustMyCode(bool justMyCode) { await SetJustMyCode(justMyCode); diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj index 6a39c840002c84..5aa5bb7b55c734 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj @@ -9,6 +9,8 @@ library true true + + <_WasmPThreadPoolSize Condition="'$(MonoWasmBuildVariant)' == 'multithread'">10 diff --git a/src/mono/wasm/runtime/debug.ts b/src/mono/wasm/runtime/debug.ts index 0bc38cf21ad9ae..76562bc8f24930 100644 --- a/src/mono/wasm/runtime/debug.ts +++ b/src/mono/wasm/runtime/debug.ts @@ -7,7 +7,7 @@ import { toBase64StringImpl } from "./base64"; import cwraps from "./cwraps"; import { VoidPtr, CharPtr } from "./types/emscripten"; import { mono_log_warn } from "./logging"; -import { localHeapViewU8 } from "./memory"; +import { forceThreadMemoryViewRefresh, localHeapViewU8 } from "./memory"; import { utf8ToString } from "./strings"; const commands_received: any = new Map(); commands_received.remove = function (key: number): CommandResponse { const value = this.get(key); this.delete(key); return value; }; @@ -75,6 +75,8 @@ function mono_wasm_malloc_and_set_debug_buffer(command_parameters: string) { } export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: number, command: number, command_parameters: string, length: number, valtype: number, newvalue: number): CommandResponseResult { + forceThreadMemoryViewRefresh(); + mono_wasm_malloc_and_set_debug_buffer(command_parameters); cwraps.mono_wasm_send_dbg_command_with_parms(id, command_set, command, _debugger_buffer, length, valtype, newvalue.toString()); @@ -85,6 +87,8 @@ export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: n } export function mono_wasm_send_dbg_command(id: number, command_set: number, command: number, command_parameters: string): CommandResponseResult { + forceThreadMemoryViewRefresh(); + mono_wasm_malloc_and_set_debug_buffer(command_parameters); cwraps.mono_wasm_send_dbg_command(id, command_set, command, _debugger_buffer, command_parameters.length); @@ -105,14 +109,16 @@ export function mono_wasm_get_dbg_command_info(): CommandResponseResult { } export function mono_wasm_debugger_resume(): void { - //nothing + forceThreadMemoryViewRefresh(); } export function mono_wasm_detach_debugger(): void { + forceThreadMemoryViewRefresh(); cwraps.mono_wasm_set_is_debugger_attached(false); } export function mono_wasm_change_debugger_log_level(level: number): void { + forceThreadMemoryViewRefresh(); cwraps.mono_wasm_change_debugger_log_level(level); } @@ -148,6 +154,7 @@ export function mono_wasm_wait_for_debugger(): Promise { export function mono_wasm_debugger_attached(): void { if (runtimeHelpers.waitForDebugger == -1) runtimeHelpers.waitForDebugger = 1; + forceThreadMemoryViewRefresh(); cwraps.mono_wasm_set_is_debugger_attached(true); } @@ -160,6 +167,8 @@ export function mono_wasm_set_entrypoint_breakpoint(assembly_name: CharPtr, entr console.assert(true, `Adding an entrypoint breakpoint ${_assembly_name_str} at method token ${_entrypoint_method_token}`); // eslint-disable-next-line no-debugger debugger; + + forceThreadMemoryViewRefresh(); } function _create_proxy_from_object_id(objectId: string, details: any) { @@ -210,6 +219,8 @@ function _create_proxy_from_object_id(objectId: string, details: any) { } export function mono_wasm_call_function_on(request: CallRequest): CFOResponse { + forceThreadMemoryViewRefresh(); + if (request.arguments != undefined && !Array.isArray(request.arguments)) throw new Error(`"arguments" should be an array, but was ${request.arguments}`); @@ -329,6 +340,7 @@ type ValueAsJsonString = { } export function mono_wasm_get_details(objectId: string, args = {}): ValueAsJsonString { + forceThreadMemoryViewRefresh(); return _get_cfo_res_details(`dotnet:cfo_res:${objectId}`, args); } @@ -344,6 +356,7 @@ export function mono_wasm_release_object(objectId: string): void { } export function mono_wasm_debugger_log(level: number, message_ptr: CharPtr): void { + forceThreadMemoryViewRefresh(); const message = utf8ToString(message_ptr); if (INTERNAL["logging"] && typeof INTERNAL.logging["debugger"] === "function") { diff --git a/src/mono/wasm/runtime/memory.ts b/src/mono/wasm/runtime/memory.ts index 4a864cf347f630..f91e68b16d2c15 100644 --- a/src/mono/wasm/runtime/memory.ts +++ b/src/mono/wasm/runtime/memory.ts @@ -408,3 +408,27 @@ export function isSharedArrayBuffer(buffer: any): buffer is SharedArrayBuffer { // See also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag return sharedArrayBufferDefined && buffer[Symbol.toStringTag] === "SharedArrayBuffer"; } + +/* +Problem: When WebWorker is suspended in the browser, the other running threads could `grow` the linear memory in the meantime. +After the thread is un-suspended C code may to try to de-reference pointer which is beyond it's known view. +This is likely V8 bug. We don't have direct evidence, just failed debugger unit tests with MT runtime. +*/ +export function forceThreadMemoryViewRefresh() { + // this condition should be eliminated by rollup on non-threading builds and it would become empty method. + if (!MonoWasmThreads) return; + + const wasmMemory = Module.getMemory(); + + /* + Normally when wasm memory grows in v8, this size change is broadcast to other web workers via an 'interrupt', which works by setting a thread-local flag that needs to be checked. + It's possible that at this point in execution the flag has not been checked yet (because this worker was suspended by the debugger in an unknown location), + which means the size change has not taken effect in this worker. + wasmMemory.grow's implementation in v8 checks to see whether other workers have already grown the buffer, + and will update the current worker's knowledge of the buffer's size. + After that we should be able to safely updateMemoryViews and get a correctly sized view. + This only works because their implementation does not skip doing work even when you ask to grow by 0 pages. + */ + wasmMemory.grow(0); + runtimeHelpers.updateMemoryViews(); +}