Skip to content

Conversation

@VSadov
Copy link
Member

@VSadov VSadov commented Dec 10, 2025

Fixes: #121760

To make the insertion of methods more robust we:

  • double the capacity of the MT builder up to the absolute limit required by the implementation of MethodDesc.
  • check if we still fit in the budget when adding methods.

Note: it may result in some programs that used to run to start throwing TypeLoadException.

These would be extreme cases though. The capacity for simple methods is implementation-specific. In CoreCLR a type cannot contain more than 65524 methods due to internal limitations.
(More complex cases involving inheritance/virtual methods may have additional limits.)

With runtime async, methods that return Task/ValueTask may consume 2 methods instead of 1 from this 65524 budget.
In a worst case where all methods return Task, the limit becomes 32762 methods in a single type, which still seems a very high limit for reasonably real programs that are not generated testcases.

@github-actions github-actions bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Dec 10, 2025
@VSadov VSadov added area-VM-coreclr and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Dec 10, 2025
@VSadov VSadov added this to the 11.0.0 milestone Dec 10, 2025
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.


// Runtime-async
RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_RuntimeAsync, W("RuntimeAsync"), 0, "Enables runtime async method support")
RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_RuntimeAsync, W("RuntimeAsync"), 1, "Enables runtime async method support")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: undo the commit that enables async

bmtMethod->m_cMaxDeclaredMethods = (SLOT_INDEX)cMethAndGaps;

DWORD cMethUpperBound = cMethAndGaps;
if (g_pConfig->RuntimeAsync())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this if-check be deleted?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess so. The new behavior does not need to be conditional.

@VSadov VSadov marked this pull request as ready for review December 11, 2025 00:42
@VSadov VSadov requested review from Copilot and jkotas December 11, 2025 00:42
for (int i = 0; pDebugBlock != &m_FirstBlock; i++)
{
CONSISTENCY_CHECK_MSGF(i < 10000, ("Linked list is much longer than expected, memory corruption likely\n"));
CONSISTENCY_CHECK_MSGF(i < 60000, ("Linked list is much longer than expected, memory corruption likely\n"));
Copy link
Member Author

@VSadov VSadov Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new capacity test was hitting this assert. I think the assert checks for loops in the list.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes a TODO comment in methodtablebuilder.cpp and makes the insertion of async method variants more robust by implementing proper capacity management and bounds checking. The changes enable RuntimeAsync by default and add comprehensive tests to verify the new method capacity limits.

  • Implements robust capacity doubling for async methods (up to MAX_SLOT_INDEX - 1 = 65524)
  • Adds runtime bounds checking before method insertion to prevent array overflow
  • Enables RuntimeAsync by default for CoreCLR and enables corresponding tests

Reviewed changes

Copilot reviewed 6 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/coreclr/vm/methodtablebuilder.cpp Replaces conditional capacity doubling with robust upfront capacity calculation clamped to MAX_SLOT_INDEX - 1, and adds bounds check before method insertion to throw TypeLoadException when limit is exceeded
src/coreclr/utilcode/allocmemtracker.cpp Increases consistency check threshold from 10,000 to 60,000 blocks to accommodate larger classes with up to 65,524 methods
src/coreclr/inc/clrconfigvalues.h Enables RuntimeAsync feature by default (changes default value from 0 to 1)
src/tests/async/Directory.Build.targets Removes the DisableProjectBuild condition to enable async tests for CoreCLR (non-nativeaot) builds
src/tests/async/capacity/capacity.csproj Defines new test project structure including test file and three generated test class files
src/tests/async/capacity/capacity.cs Adds three test scenarios: 40,000 int-returning methods (should succeed), 32,750 Task-returning methods (should succeed), and 32,763 Task-returning methods (should fail with TypeLoadException)

// The method count is typically a modest number though.
// We will reserve twice the size for the builder, up to the max, just in case.
DWORD cMethUpperBound = cMethAndGaps * 2;
if (cMethUpperBound >= (DWORD)MAX_SLOT_INDEX)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (cMethUpperBound >= (DWORD)MAX_SLOT_INDEX)
if ((DWORD)MAX_SLOT_INDEX <= cMethUpperBound)

So that it is same style as if ((DWORD)MAX_SLOT_INDEX <= cMethAndGaps) a few lines above

}

[Fact]
public static void TestLargeClassWithTaskMethods_Exception()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is testing CoreCLR specific implementation limitation. Is it going to pass on Native AOT? If not, it should be disabled there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And if it is testing CoreCLR specific implementation limitation, we may consider using Reflection.Emit to generate the test types on the fly to avoid these very large source files being checked in.

Copy link
Member

@jkotas jkotas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM otherwise. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[RuntimeAsync] TODO in methodtablebuildr.cpp

2 participants