Reconcile ReleaseHolder, ComHolderPreemp, and ComHolderAnyMode#130373
Reconcile ReleaseHolder, ComHolderPreemp, and ComHolderAnyMode#130373AaronRobinsonMSFT wants to merge 8 commits into
Conversation
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>
|
Tagging subscribers to this area: @agocke |
There was a problem hiding this comment.
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
ReleaseHolderto aLifetimeHolder<ReleaseHolderTraits<T>>implementation and introducesHolderDetail::HasReleaseMethodfor duck-typedRelease()detection. - Renames / consolidates COM holders (
ComHolderAnyMode→ReleaseHolderAnyMode; removesComHolderPreemp) and migrates call sites toDetach()/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
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>
- 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.
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
Co-authored-by: Aaron R Robinson <arobins@microsoft.com>
| // 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) |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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 | |||
There was a problem hiding this comment.
| #define AFTER_CONTRACTS |
Dead code?
| struct ReleaseHolderTraits final | ||
| { | ||
| static_assert( | ||
| HolderDetail::HasReleaseMethod<TYPE>::value, |
There was a problem hiding this comment.
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.)
Converge the three COM-interface RAII holders (
ReleaseHolder,ComHolderPreemp,ComHolderAnyMode) onto theLifetimeHolder<Traits>pattern.Changes
ReleaseHolderis retargeted toLifetimeHolder<ReleaseHolderTraits<T>>, adoptingComHolderPreemp's preemptive-mode release semantics (the VM default).Free()uses aMODE_PREEMPTIVEcontract gated onENABLE_CONTRACTS_IMPL, so non-VM consumers (utilcode, md, DAC, createdump) fall back to a static-contract annotation.ComHolderPreempis removed;ReleaseHolderreplaces all uses.ComHolderAnyModeis renamed toReleaseHolderAnyMode.static_assertis relaxed fromis_base_of<IUnknown, T>to a duck-typed "Thas aRelease()member" check (holder_detail::HasReleaseMethod), soReleaseHoldercan hold non-IUnknowntypes that exposeRelease().