[cDAC] Enable cache fallback on RuntimeTypeSystem and ExecutionManager contracts - #132258
[cDAC] Enable cache fallback on RuntimeTypeSystem and ExecutionManager contracts#132258rcj1 wants to merge 1 commit into
Conversation
|
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. |
|
Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag |
There was a problem hiding this comment.
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_1for missingMethodTable/MethodDescentries after a cache clear. - Add on-demand cache fallback helper in
ExecutionManagerCoreto recomputeCodeBlockdata 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)) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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.