Migrate SafeComHolder and utilcode holders to LifetimeHolder - #130316
#130316Merged
AaronRobinsonMSFT merged 11 commits intoJul 8, 2026
dotnet:maindotnet/runtime:mainfrom
AaronRobinsonMSFT:safecomholderpreemp-lifetimeAaronRobinsonMSFT/runtime:safecomholderpreemp-lifetimeCopy head branch name to clipboard
Merged
Migrate SafeComHolder and utilcode holders to LifetimeHolder#130316AaronRobinsonMSFT merged 11 commits intodotnet:maindotnet/runtime:mainfrom AaronRobinsonMSFT:safecomholderpreemp-lifetimeAaronRobinsonMSFT/runtime:safecomholderpreemp-lifetimeCopy head branch name to clipboard
AaronRobinsonMSFT merged 11 commits into
dotnet:maindotnet/runtime:mainfrom
AaronRobinsonMSFT:safecomholderpreemp-lifetimeAaronRobinsonMSFT/runtime:safecomholderpreemp-lifetimeCopy head branch name to clipboard
Conversation
Continues the migration of legacy `Wrapper`/`SpecializedWrapper`-based RAII holders to the trait-based `LifetimeHolder<Traits>` pattern. `SafeComHolderPreemp<T>` becomes `LifetimeHolder<SafeComHolderPreempTraits<T>>`, whose `Free` releases the COM interface in preemptive mode via `SafeReleasePreemp` (NULL-safe). Call sites are updated for the `LifetimeHolder` API: copy-initialization from `NULL`/a raw pointer becomes default- or brace-initialization, `SuppressRelease()`/`GetValue()` become `Detach()`, and `IsNull()` becomes a `nullptr` comparison. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
These call sites destruct their holder while the thread is in preemptive mode (either the enclosing function is MODE_PREEMPTIVE, or the holder is declared after a GCX_PREEMP / before a scoped GCX_COOP that reverts), so they can use the preemptive-only release path without the coop->preempt bounce performed by SafeRelease. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the SpecializedWrapper-based SafeComHolder with a mode-agnostic SafeComHolderAny built on LifetimeHolder. The SafeComHolderAnyTraits Free() runs under MODE_ANY and calls SafeRelease, which transitions to preemptive internally when required. Rename all call sites, drop legacy = NULL initializers, translate the old Extract/SuppressRelease/Clear/Release API to Detach/Free, and add an explicit (void**) cast where operator& is passed to a void** out-parameter. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the SpecializedWrapper-based ReservedMemoryHolder and ThunkMemoryHolder with LifetimeHolder traits. Because these sites called SuppressRelease() and then continued to use the pointer value (which LifetimeHolder's Detach() clears), split each site into a raw BYTE* value plus a holder that owns the memory purely as an error-path guard, relinquishing ownership via Detach() on success. Add a NULL guard to ReleaseAllocatedThunks since LifetimeHolder always invokes the trait's Free() on destruction. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
|
Tagging subscribers to this area: @agocke |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates CoreCLR native RAII usage by migrating legacy Wrapper/SpecializedWrapper COM + utilcode loader-heap holders to the trait-based LifetimeHolder<Traits> pattern, and adjusts call sites to the new holder API (Detach, Free, etc.).
Changes:
- Replace legacy
SafeComHolder*wrappers withSafeComHolderAny/SafeComHolderPreempimplemented viaLifetimeHoldertraits. - Update COM interop call sites to use
LifetimeHolderAPIs (Detach,Free) and pointer comparisons (!= nullptr). - Migrate utilcode loader-heap holders to
LifetimeHoldertraits and refactor ownership/guard patterns to avoidSuppressRelease()usage.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/vm/wrappers.h | Defines SafeComHolderAny/SafeComHolderPreemp as LifetimeHolder-based traits. |
| src/coreclr/vm/weakreferencenative.cpp | Updates weak-ref QCalls to use SafeComHolderPreemp and Detach patterns. |
| src/coreclr/vm/stubmgr.cpp | Switches COM holders to mode-agnostic SafeComHolderAny. |
| src/coreclr/vm/stubhelpers.cpp | Uses SafeComHolderAny and Detach() for COM interface ownership transfer. |
| src/coreclr/vm/stdinterfaces.cpp | Converts various COM holders to SafeComHolderAny/SafeComHolderPreemp and replaces SuppressRelease() with Detach(). |
| src/coreclr/vm/runtimecallablewrapper.cpp | Replaces legacy holders with SafeComHolderAny/SafeComHolderPreemp and updates ownership flows to Detach/Free. |
| src/coreclr/vm/peimage.cpp | Migrates metadata COM holder to SafeComHolderAny. |
| src/coreclr/vm/peassembly.cpp | Migrates metadata emit holder to SafeComHolderAny. |
| src/coreclr/vm/olevariant.cpp | Updates COM holders in OLE variant marshalling to SafeComHolderAny/SafeComHolderPreemp. |
| src/coreclr/vm/interoputil.cpp | Migrates various COM holders to SafeComHolderAny and updates extraction to Detach(). |
| src/coreclr/vm/interopconverter.cpp | Updates COM IP acquisition helpers to use SafeComHolderAny and Detach(). |
| src/coreclr/vm/encee.cpp | Uses SafeComHolderAny for ENC metadata holders and updates comment. |
| src/coreclr/vm/dispparammarshaler.cpp | Converts COM holders to SafeComHolderAny and replaces SuppressRelease() with Detach(). |
| src/coreclr/vm/commodule.cpp | Migrates metadata import holder to SafeComHolderAny. |
| src/coreclr/vm/comconnectionpoints.cpp | Migrates event interface holder to SafeComHolderAny. |
| src/coreclr/vm/comcallablewrapper.cpp | Migrates COM holders to SafeComHolderAny and updates extraction to Detach(). |
| src/coreclr/vm/comcache.cpp | Migrates stream/error-info holders to SafeComHolderAny/SafeComHolderPreemp and updates release semantics to Free(). |
| src/coreclr/vm/clrex.cpp | Migrates error-info holder to SafeComHolderAny. |
| src/coreclr/vm/ceeload.cpp | Migrates symbol-reader and stream holders to SafeComHolderAny and switches extraction to Detach(). |
| src/coreclr/vm/assembly.cpp | Migrates metadata dispenser/emit holders to SafeComHolderAny and adjusts void** out-param usage. |
| src/coreclr/utilcode/loaderheap.cpp | Migrates reserved-memory holder pattern to LifetimeHolder with explicit guard + raw pointer split. |
| src/coreclr/utilcode/loaderheap_shared.h | Defines ReservedMemoryHolder as a LifetimeHolder over ReservedMemoryTraits. |
| src/coreclr/utilcode/interleavedloaderheap.cpp | Migrates reserved/thunk memory holders to LifetimeHolder traits and adds null guard for thunk free. |
| src/coreclr/utilcode/explicitcontrolloaderheap.cpp | Migrates reserved-memory holder pattern to LifetimeHolder with explicit guard + raw pointer split. |
| src/coreclr/inc/holder.h | Updates guidance comment referencing SafeComHolderAny. |
| src/coreclr/debug/ee/debugger.cpp | Switches symbol-reader holder to SafeComHolderPreemp under an existing preemptive GC scope. |
Copilot's findings
- Files reviewed: 26/26 changed files
- Comments generated: 1
wrappers.h uses LifetimeHolder and std::is_base_of directly but relied on transitive includes for both. Include holder.h so the header is self-contained. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Freed in cooperative GC mode, so the MODE_PREEMPTIVE trait tripped a contract fail-fast on checked builds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jkotas
reviewed
Jul 8, 2026
jkotas
reviewed
Jul 8, 2026
…and consistency across the codebase.
jkotas
reviewed
Jul 8, 2026
jkotas
reviewed
Jul 8, 2026
jkotas
reviewed
Jul 8, 2026
jkotas
reviewed
Jul 8, 2026
- Replaced instances of SafeComHolderAnyMode with ComHolderAnyMode across multiple files to improve clarity and consistency in COM interface management. - Updated comments to reflect the new naming conventions for COM holder classes. - Ensured that the new ComHolderAnyMode and ComHolderPreemp are used in contexts where preemptive mode is necessary, maintaining the intended functionality. - Adjusted related documentation to guide developers on the appropriate usage of the new holder classes.
jkotas
approved these changes
Jul 8, 2026
jkotas
approved these changes
Jul 8, 2026
eiriktsarpalis
pushed a commit
that referenced
this pull request
Jul 15, 2026
Continues the incremental migration of CoreCLR's legacy `Wrapper` / `SpecializedWrapper`-based RAII holders to the trait-based `LifetimeHolder<Traits>` pattern. - **`ComHolderPreemp<T>` → `LifetimeHolder<ComHolderPreempTraits<T>>`.** `Free` releases the COM interface in preemptive mode via `SafeReleasePreemp` (NULL-safe). - **Convert clearly-preemptive `SafeComHolder` sites to `ComHolderPreemp`.** These call sites destruct their holder while the thread is in preemptive mode (enclosing function is `MODE_PREEMPTIVE`, or the holder is scoped after a `GCX_PREEMP`), so they can use the preemptive-only release path and avoid the coop→preempt bounce in `SafeRelease`. - **`SafeComHolder` → `ComHolderAnyMode` (mode-agnostic).** `ComHolderAnyModeTraits::Free()` runs under `MODE_ANY` and calls `SafeRelease`, which transitions to preemptive internally when required. - **utilcode loader-heap holders** (`ReservedMemoryHolder`, `ThunkMemoryHolder`) migrated to `LifetimeHolder` traits. Sites that previously called `SuppressRelease()` and kept using the pointer are split into a raw `BYTE*` value plus a holder that owns the memory purely as an error-path guard, relinquishing ownership via `Detach()` on success. Added a NULL guard to `ReleaseAllocatedThunks` since `LifetimeHolder` always invokes the trait's `Free()` on destruction. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Continues the incremental migration of CoreCLR's legacy
Wrapper/SpecializedWrapper-based RAII holders to the trait-basedLifetimeHolder<Traits>pattern.This PR covers the COM
SafeComHolderfamily and the utilcode loader-heap holders:ComHolderPreemp<T>→LifetimeHolder<ComHolderPreempTraits<T>>.Freereleases the COM interface in preemptive mode viaSafeReleasePreemp(NULL-safe).SafeComHoldersites toComHolderPreemp. These call sites destruct their holder while the thread is in preemptive mode (enclosing function isMODE_PREEMPTIVE, or the holder is scoped after aGCX_PREEMP), so they can use the preemptive-only release path and avoid the coop→preempt bounce inSafeRelease.SafeComHolder→ComHolderAnyMode(mode-agnostic).ComHolderAnyModeTraits::Free()runs underMODE_ANYand callsSafeRelease, which transitions to preemptive internally when required.ReservedMemoryHolder,ThunkMemoryHolder) migrated toLifetimeHoldertraits. Sites that previously calledSuppressRelease()and kept using the pointer are split into a rawBYTE*value plus a holder that owns the memory purely as an error-path guard, relinquishing ownership viaDetach()on success. Added a NULL guard toReleaseAllocatedThunkssinceLifetimeHolderalways invokes the trait'sFree()on destruction.Call sites are updated for the
LifetimeHolderAPI throughout: copy-initialization fromNULL/ a raw pointer becomes default- or brace-initialization;SuppressRelease()/GetValue()becomeDetach();IsNull()becomes anullptrcomparison;Extract/Clear/ReleasebecomeDetach/Free; and an explicit(void**)cast is added whereoperator&is passed to avoid**out-parameter.Note
This PR description was generated with assistance from GitHub Copilot.