Convert constructor invocations from the runtime to use UnmanagedCallersOnly#124920
Convert constructor invocations from the runtime to use UnmanagedCallersOnly#124920jkoritzinsky merged 16 commits intodotnet:mainfrom
Conversation
…RE_NONVIRTUAL_CALLSITE_USING_METHODDESC to use UnmanagedCallersOnly
|
Tagging subscribers to this area: @agocke |
There was a problem hiding this comment.
Pull request overview
This PR continues the migration away from MethodDescCallSite / CallDescrWorker-style VM-to-managed invocation, switching class constructor and default instance constructor calls over to the UnmanagedCallersOnly reverse P/Invoke pattern (per #123864). It also removes the now-unused CATCH_HANDLER_FOUND_NOTIFICATION_CALLSITE / PREPARE_NONVIRTUAL_CALLSITE_USING_METHODDESC infrastructure.
Changes:
- Route
MethodTable::RunClassInitEx(.cctor invocation) through a newInitHelpers.CallClassConstructor[UnmanagedCallersOnly]entrypoint andUnmanagedCallersOnlyCaller. - Route default constructor invocations through a new
RuntimeHelpers.CallDefaultConstructor[UnmanagedCallersOnly]entrypoint andUnmanagedCallersOnlyCaller. - Remove debugger notification dispatch flag and wrapper logic from
DispatchCallSimple/call helper macros.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/vm/methodtable.cpp | Switches class constructor invocation to UnmanagedCallersOnlyCaller. |
| src/coreclr/vm/corelib.h | Adds CoreLib binder entries for the new UCO-managed helpers. |
| src/coreclr/vm/cominterfacemarshaler.cpp | Switches extensible RCW default ctor invocation to UCO path. |
| src/coreclr/vm/callhelpers.h | Removes catch-handler-found notification flag/macro; retains UCO caller helper. |
| src/coreclr/vm/callhelpers.cpp | Removes debugger wrapper dispatch path; switches default ctor helper to UCO path. |
| src/coreclr/System.Private.CoreLib/.../RuntimeHelpers.CoreCLR.cs | Adds [UnmanagedCallersOnly] method for default ctor invocation. |
| src/coreclr/System.Private.CoreLib/.../InitHelpers.cs | Adds [UnmanagedCallersOnly] method for class constructor invocation. |
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/InitHelpers.cs
Outdated
Show resolved
Hide resolved
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/InitHelpers.cs
Show resolved
Hide resolved
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/InitHelpers.cs
Outdated
Show resolved
Hide resolved
...coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
Show resolved
Hide resolved
We would have to use reflection ( |
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/InitHelpers.cs
Outdated
Show resolved
Hide resolved
Okay. |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/InitHelpers.cs
Show resolved
Hide resolved
|
@AaronRobinsonMSFT any idea why we'd be getting asserts for the incorrect GC mode on thread shutdown on some wasm tests with this change? |
I would guess we're missing a transition call somewhere. Do you have a stack? |
Only stacks I have are on the failures on CI, which would be the Maybe the problem is some sort of crash on WASM only that takes down the VM and hits this assert before it can report anything useful about the actual test failure? |
Quite possibly. This looks like something that you should be able to create a small repo and debug on node.js through corerun. |
|
Ok it looks like this failure is unrelated to this PR, but this PR exposes it. Basically, when we get the exception back from the cctor, we try to throw it. As part of that, we try to get the stack trace. During that call in the failing test suites, we allocate enough memory to trigger the GC. The GC tries to trigger the finalizer. For some reason, there's nothing marking the runtime to be kept alive, so it gets shut down after the finalization tick. However, the finalizer has left the GC mode for the thread in the incorrect state (looking into why now). I guess by going down I'll continue investigating. |
|
Yep, when we enter |
|
Looks like I was getting the async stack trace, so the issue is not that we're recursing into the finalizer tick while the GC is running, but instead we're triggering the next loop in the event loop from a thread in COOP mode. I'll open a known issue if I can't figure this out in a reasonable time frame. |
…le it and will crash
|
Found it: Fix in f3405df Without that fix, Emscripten will throw a MemoryError that we don't handle, which takes down the runtime in its current state (no C++ unwinding). Then, we were running the next tick which ran the finalizer (entering the runtime in a bad state). |
|
@jkoritzinsky, has it covered these: |
|
@am11 this didn't cover those. I've been looking at the next phase of work, not the items in that list. |

This PR moves the class constructor and instance constructor invocations from within the runtime to use the UnmanagedCallersOnly pattern established with #123864 to invoke the constructors.
Contributes to #123864.
As this PR removes all usages of
CATCH_HANDLER_FOUND_NOTIFICATION_CALLSITEandPREPARE_NONVIRTUAL_CALLSITE_USING_METHODDESC, these pieces of infrastructure are removed as well.