Skip to content

Reconcile ReleaseHolder, ComHolderPreemp, and ComHolderAnyMode#130373

Draft
AaronRobinsonMSFT wants to merge 2 commits into
dotnet:mainfrom
AaronRobinsonMSFT:reconcile-releaseholder
Draft

Reconcile ReleaseHolder, ComHolderPreemp, and ComHolderAnyMode#130373
AaronRobinsonMSFT wants to merge 2 commits into
dotnet:mainfrom
AaronRobinsonMSFT:reconcile-releaseholder

Conversation

@AaronRobinsonMSFT

@AaronRobinsonMSFT AaronRobinsonMSFT commented Jul 8, 2026

Copy link
Copy Markdown
Member

Converge the three COM-interface RAII holders (ReleaseHolder, ComHolderPreemp, ComHolderAnyMode) onto the LifetimeHolder<Traits> pattern.

Changes

  • ReleaseHolder is retargeted to LifetimeHolder<ReleaseHolderTraits<T>>, adopting ComHolderPreemp's preemptive-mode release semantics (the VM default). Free() uses a MODE_PREEMPTIVE contract gated on ENABLE_CONTRACTS_IMPL, so non-VM consumers (utilcode, md, DAC, createdump) fall back to a static-contract annotation.
  • ComHolderPreemp is removed; ReleaseHolder replaces all uses.
  • ComHolderAnyMode is renamed to ReleaseHolderAnyMode.
  • Both holders' static_assert is relaxed from is_base_of<IUnknown, T> to a duck-typed "T has a Release() member" check (holder_detail::HasReleaseMethod), so ReleaseHolder can hold non-IUnknown types that expose Release().

Converge the three COM-interface RAII holders onto the LifetimeHolder<Traits>
pattern:

- Retarget ReleaseHolder to LifetimeHolder<ReleaseHolderTraits<T>>, adopting
  ComHolderPreemp's preemptive-mode release semantics (the VM default). The
  Free() contract is MODE_PREEMPTIVE, gated on ENABLE_CONTRACTS_IMPL so non-VM
  consumers (utilcode, md, DAC, createdump) use a static-contract annotation.
- Remove ComHolderPreemp entirely; ReleaseHolder replaces all uses.
- Rename ComHolderAnyMode to ReleaseHolderAnyMode.
- Relax both holders' static_assert from is_base_of<IUnknown, T> to a duck-typed
  "T has a Release() member" check (holder_detail::HasReleaseMethod), so ReleaseHolder
  can hold non-IUnknown types that expose Release().

Migrate all call sites off the legacy SpecializedWrapper API onto the LifetimeHolder
API (Extract->Detach, Clear->Free, GetValue/IsNull/Assign/SuppressRelease removed).
Read-after-suppress sites are restructured to detach into a raw pointer to preserve
exception-safety and refcount behavior. BindAssemblySpec is refactored so the
CoreLib singleton borrow no longer relies on SuppressRelease.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
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 refactors CoreCLR’s COM / ref-counted RAII “holder” usage to converge on the LifetimeHolder<Traits> pattern: replacing legacy SpecializedWrapper APIs (Extract, Clear, GetValue, etc.), removing ComHolderPreemp, and renaming ComHolderAnyMode to ReleaseHolderAnyMode.

Changes:

  • Retargets ReleaseHolder to a LifetimeHolder<ReleaseHolderTraits<T>> implementation and introduces HolderDetail::HasReleaseMethod for duck-typed Release() detection.
  • Renames / consolidates COM holders (ComHolderAnyModeReleaseHolderAnyMode; removes ComHolderPreemp) and migrates call sites to Detach() / Free() patterns.
  • Refactors a few lifetime/ownership patterns (notably AppDomain::BindAssemblySpec) to avoid relying on legacy holder APIs.
Show a summary per file
File Description
src/coreclr/vm/wrappers.h Renames ComHolderAnyModeTraits/alias to ReleaseHolderAnyMode* and removes ComHolderPreemp traits.
src/coreclr/vm/weakreferencenative.cpp Migrates ComHolderPreemp<> usages to ReleaseHolder<>.
src/coreclr/vm/stubmgr.cpp Migrates ComHolderAnyMode<> usages to ReleaseHolderAnyMode<>.
src/coreclr/vm/stubhelpers.cpp Migrates ComHolderAnyMode<> usages to ReleaseHolderAnyMode<>.
src/coreclr/vm/stdinterfaces.cpp Migrates COM holders to ReleaseHolder<> / ReleaseHolderAnyMode<>.
src/coreclr/vm/runtimecallablewrapper.cpp Migrates COM holders and adjusts a CCWHolder lifetime flow to use Detach().
src/coreclr/vm/rejit.cpp Removes legacy = NULL initialization pattern for ReleaseHolder.
src/coreclr/vm/peimagelayout.cpp Replaces Extract() with Detach() when returning owned layouts.
src/coreclr/vm/peimage.cpp Migrates metadata COM holder to ReleaseHolderAnyMode<>.
src/coreclr/vm/peassembly.cpp Migrates ComHolderPreemp<> to ReleaseHolder<>.
src/coreclr/vm/olevariant.cpp Migrates COM holders to ReleaseHolder<> / ReleaseHolderAnyMode<>.
src/coreclr/vm/olecontexthelpers.cpp Migrates ComHolderPreemp<> to ReleaseHolder<>.
src/coreclr/vm/nativeimage.cpp Replaces legacy holder APIs (IsNull/Extract) with == NULL and Detach().
src/coreclr/vm/marshalnative.cpp Updates CCWHolder initialization to brace-init and adjusts holder usage.
src/coreclr/vm/interoputil.cpp Call sites migrated to new holder type names.
src/coreclr/vm/interopconverter.cpp Migrates COM holders and replaces legacy holder APIs in a few places.
src/coreclr/vm/encee.cpp Renames holder types in EnC path comments and variables.
src/coreclr/vm/eetoprofinterfaceimpl.cpp Replaces Extract()+NULL patterns with Detach() for ownership transfer.
src/coreclr/vm/dllimport.cpp Updates holder assignment to use operator= instead of Assign.
src/coreclr/vm/dispparammarshaler.cpp Migrates COM holders to ReleaseHolderAnyMode<>.
src/coreclr/vm/crossloaderallocatorhash.inl Updates holder initialization style and uses Detach() for returns.
src/coreclr/vm/coreassemblyspec.cpp Uses Detach() when returning owned binder assembly.
src/coreclr/vm/commodule.cpp Migrates COM holders to ReleaseHolder<> / ReleaseHolderAnyMode<>.
src/coreclr/vm/comconnectionpoints.cpp Migrates ComHolderAnyMode<> to ReleaseHolderAnyMode<>.
src/coreclr/vm/comcallablewrapper.cpp Migrates holders and refactors template-return ownership to use Detach().
src/coreclr/vm/comcache.cpp Migrates COM holders and replaces Extract() with Detach().
src/coreclr/vm/clrex.cpp Migrates error-info holder to ReleaseHolderAnyMode<>.
src/coreclr/vm/ceeload.cpp Migrates symbol reader holders to ReleaseHolder<> and modernizes initialization.
src/coreclr/vm/assemblynative.cpp Passes holder directly to tracing (via implicit pointer conversion).
src/coreclr/vm/assembly.cpp Uses Detach() for ownership transfer and brace-init for holders.
src/coreclr/vm/appdomain.hpp Adjusts FindCachedFile signature (removes unused fThrow).
src/coreclr/vm/appdomain.cpp Refactors BindAssemblySpec lifetime management; simplifies FindCachedFile.
src/coreclr/md/enc/mdinternalrw.cpp Fixes QueryInterface call to pass void**.
src/coreclr/inc/holder.h Removes legacy ReleaseHolder wrapper, adds LifetimeHolder-based ReleaseHolder + HasReleaseMethod.
src/coreclr/dlls/mscoree/exports.cpp Uses Detach() to transfer host handle ownership.
src/coreclr/debug/ee/debugger.cpp Migrates ComHolderPreemp<> to ReleaseHolder<>.
src/coreclr/debug/daccess/enummem.cpp Replaces legacy holder APIs (Clear/GetValue) with Free() and direct holder conversions.
src/coreclr/debug/daccess/dacdbiimpl.cpp Removes = nullptr initialization for ReleaseHolder.
src/coreclr/debug/daccess/daccess.cpp Removes = nullptr initialization for ReleaseHolder.
src/coreclr/debug/daccess/cdac.h Replaces Extract() with Detach() in move operations.
src/coreclr/debug/createdump/threadinfo.cpp Replaces Extract() with Detach() when transferring method ownership into a frame.
src/coreclr/debug/createdump/createdumpunix.cpp Updates holder initialization to brace-init.
src/coreclr/debug/createdump/crashinfo.cpp Removes = nullptr initialization for ReleaseHolder.
src/coreclr/binder/defaultassemblybinder.cpp Replaces Extract() with Detach() for binder assembly ownership transfer.
src/coreclr/binder/customassemblybinder.cpp Replaces Extract() with Detach() for binder assembly ownership transfer.
src/coreclr/binder/assemblybindercommon.cpp Replaces Extract() with Detach() for binder assembly ownership transfer.
src/coreclr/binder/assembly.cpp Replaces Extract() with Detach() for assembly-name ownership transfer.

Copilot's findings

  • Files reviewed: 47/47 changed files
  • Comments generated: 4

Comment thread src/coreclr/inc/holder.h Outdated
Comment thread src/coreclr/vm/wrappers.h
Comment thread src/coreclr/inc/holder.h Outdated
Comment thread src/coreclr/vm/appdomain.cpp Outdated
holder.h now includes contract.h (for the MODE_PREEMPTIVE CONTRACTL in
ReleaseHolderTraits::Free), which pulled check.inl -> clrhost.h -> holder.h
back into itself before Holder/LifetimeHolder were defined. Remove the
clrhost.h include from check.inl to break the cycle, and make the two
headers that were relying on that transitive path self-sufficient:

- inc/sigbuilder.h: include corhdr.h for CorElementType/mdToken.
- jit/jitstd/algorithm.h: include <utility> for std::swap used by jitstd::sort.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 8, 2026 20:37

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.

Copilot's findings

  • Files reviewed: 50/50 changed files
  • Comments generated: 2

Comment thread src/coreclr/inc/holder.h
Comment thread src/coreclr/vm/wrappers.h
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants