Skip to content

[cDAC] Enable cache fallback on RuntimeTypeSystem and ExecutionManager contracts - #132258

Closed
rcj1 wants to merge 1 commit into
dotnet:mainfrom
rcj1:enable-cdac-contract-cache-fallback
Closed

[cDAC] Enable cache fallback on RuntimeTypeSystem and ExecutionManager contracts#132258
rcj1 wants to merge 1 commit into
dotnet:mainfrom
rcj1:enable-cdac-contract-cache-fallback

Conversation

@rcj1

@rcj1 rcj1 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

All other contracts have a retry mechanism whereby if something is not cached in the contract cache, we try to retrieve the data from the target. Only these two were missing this, which opens up the door to weird cases if a Flush() is done simultaneously with another operation. This is part 1 of hardening to this scenario - part 2 is to use ConcurrentDictionary for cDAC caches.

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 4 pipeline(s).
12 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 hardens the cDAC RuntimeTypeSystem and ExecutionManager contracts against cache invalidation by adding “re-fetch on cache miss” behavior, and updates unit tests to validate behavior after Flush().

Changes:

  • Add on-demand cache fallback helpers in RuntimeTypeSystem_1 for missing MethodTable/MethodDesc entries after a cache clear.
  • Add on-demand cache fallback helper in ExecutionManagerCore to recompute CodeBlock data when a handle isn’t present in the cache (e.g., post-Flush).
  • Extend existing unit tests to exercise contract APIs after Flush(FlushScope.All).
Show a summary per file
File Description
src/native/managed/cdac/tests/UnitTests/MethodTableTests.cs Adds a post-Flush assertion to ensure IsString works after cache invalidation.
src/native/managed/cdac/tests/UnitTests/MethodDescTests.cs Adds post-Flush assertions to ensure MethodDesc queries still succeed after cache invalidation.
src/native/managed/cdac/tests/UnitTests/ExecutionManager/ExecutionManagerTests.cs Adds a post-Flush assertion to ensure GetMethodDesc works after cache invalidation.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs Implements cache-miss fallback for method table / method desc lookups by re-reading target data.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs Implements cache-miss fallback for CodeBlockHandle-based lookups by recomputing CodeBlock data.

Review details

  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Lite


private CodeBlock GetOrCreateCodeBlock(CodeBlockHandle codeInfoHandle)
{
if (_codeInfos.TryGetValue(codeInfoHandle.Address, out CodeBlock? info))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should make a decision whether we expect CodeBlockHandle (or other XyzHandle types) to remain valid across a flush operation. I'd propose no, all handles should become invalid on Flush. If the caller expected that there was still code in the same location before and after a flush then they would need to preserve the address or whatever other identity information created the handle and call into cDAC to re-create the handle (which would also repopulate any Dictionary backed cache at the same time).

This probably means instead of trying to repopulate the handle on the fly we should go modify other handles so they don't try to auto-repopulate if they are currently doing so. We could also add a flush cookie that updates on every Flush() call so that handles can be tagged with the cookie that was active when they were created and rejected if a caller tries to reuse a stale handle.

@rcj1 rcj1 Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The question of whether we "expect" a CodeBlockHandle or other handle to stay valid depends on what we mean by "expect". We cannot prove yes or no without invoking more complex logic; we have to know where the object was allocated from, whether we are doing time-travel debugging, etc. However, I would assume it probably does more often than not, and that the same holds for the RuntimeTypeSystem caches.

The repopulation is a best-effort attempt at driving each individual DacDbi API to completion in the case that it is still valid. If it is no longer valid, then we return an error or invalid data, and that is a bug on the caller's side (for example in the DBI). We can wrap any error in the repopulation in a more descriptive error message. But at that point, it does not really matter what we do.

This is not intended to replace the process or stop-go locks that synchronize the DBI, more as a fail-safe to mostly produce a correct answer despite any locking bugs.

@rcj1 rcj1 closed this Aug 13, 2026
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.

3 participants