Skip to content

Reconcile ReleaseHolder, ComHolderPreemp, and ComHolderAnyMode#130373

Open
AaronRobinsonMSFT wants to merge 8 commits into
dotnet:mainfrom
AaronRobinsonMSFT:reconcile-releaseholder
Open

Reconcile ReleaseHolder, ComHolderPreemp, and ComHolderAnyMode#130373
AaronRobinsonMSFT wants to merge 8 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 Outdated
Comment thread src/coreclr/vm/wrappers.h
- Added DBI_COMPILE definition for conditional compilation in clrdefinitions.cmake.
- Updated Debugger::GetAndSendTransitionStubInfo to use g_pEEInterface for IP validation.
- Refined contract enabling conditions in contract.h to include DBI builds.
- Modified ReleaseHolderTraits to use ENABLE_EE_CONTRACTS for contract checks.
- Removed unused IsIPInModule function from utilcode.h and implemented it in ExecutionManager.
- Updated various files to consistently use ExecutionManager::IsIPInModule for IP validation.
Copilot AI review requested due to automatic review settings July 10, 2026 17:46

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: 62/62 changed files
  • Comments generated: 2

Comment thread src/coreclr/vm/wrappers.h
Comment thread src/coreclr/vm/ceeload.cpp Outdated
ReleaseHolder's Detach() clears the holder's value (unlike the old
Extract(), which retained it via SuppressRelease). BindAssemblyByProbingPaths
detached pAssembly into *ppAssembly and then dereferenced pAssembly for the
ref-def check, hitting the operator-> null assert (holder.h:974) on the TPA
bind path at startup, crashing every coreclr test process.

Use a local for the detached pointer and read from it. Also sequence the
image reads before Detach() in nativeimage.cpp OpenR2RFromPE, where the
member accesses and Detach() were unsequenced call arguments.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6457cb5c-e732-41ad-8cc0-af22e64c1f49
Copilot AI review requested due to automatic review settings July 10, 2026 21:38

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: 62/62 changed files
  • Comments generated: 2

Comment thread src/coreclr/vm/wrappers.h
Comment thread src/coreclr/inc/holder.h
Copilot AI review requested due to automatic review settings July 12, 2026 01:00
@AaronRobinsonMSFT AaronRobinsonMSFT marked this pull request as ready for review July 12, 2026 01:00

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: 62/62 changed files
  • Comments generated: 1

Comment thread src/coreclr/vm/wrappers.h
Copilot AI review requested due to automatic review settings July 13, 2026 14:28

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: 62/62 changed files
  • Comments generated: 1

Comment thread src/coreclr/inc/holder.h
Comment thread src/coreclr/vm/ceeload.cpp Outdated
Comment thread src/coreclr/vm/ceeload.cpp Outdated
Co-authored-by: Aaron R Robinson <arobins@microsoft.com>
Copilot AI review requested due to automatic review settings July 13, 2026 15:30

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: 62/62 changed files
  • Comments generated: 3

Comment thread src/coreclr/vm/wrappers.h
Comment thread src/coreclr/inc/holder.h
Comment thread src/coreclr/inc/holder.h

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: 62/62 changed files
  • Comments generated: 2

Comment thread src/coreclr/vm/wrappers.h
Comment thread src/coreclr/inc/holder.h
// Also, we won't enable contracts if this is a DAC build.
#if defined(ENABLE_CONTRACTS_DATA) && !defined(DACCESS_COMPILE) && !defined(CROSS_COMPILE)
// Also, we won't enable contracts if a DAC or DBI build, or a cross build.
#if defined(ENABLE_CONTRACTS_DATA) && !defined(DACCESS_COMPILE) && !defined(DBI_COMPILE) && !defined(CROSS_COMPILE)

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.

DAC and DBI are the only cross-compiled components. CROSS_COMPILE condition is redundant.



// We only enable contracts in _DEBUG builds
// We only enable contract data in _DEBUG builds and non-JIT builds.

@jkotas jkotas Jul 14, 2026

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.

This comment is 100% useless since it just rephrases the simple condition in plain English.

A good comment would explain "why" we are doing this.

@@ -2092,6 +2090,5 @@ extern Volatile<LONG> g_DbgSuppressAllocationAsserts;
STATIC_CONTRACT_MODE_PREEMPTIVE;

#define AFTER_CONTRACTS

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.

Suggested change
#define AFTER_CONTRACTS

Dead code?

Comment thread src/coreclr/inc/holder.h
struct ReleaseHolderTraits final
{
static_assert(
HolderDetail::HasReleaseMethod<TYPE>::value,

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.

Is the error message from the C++ compiler too inscrutable otherwise? (I am fine with this diagnostic, I am just wondering how much of an improvement it is.)

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.

3 participants