-
Notifications
You must be signed in to change notification settings - Fork 2.7k
[WIP] Add support for collectible assemblies via AssemblyLoadContext #1684
Changes from 16 commits
ececae0
4d79cee
dc7c062
57b2f1e
095eb48
748b05f
e3ac590
264f969
d35f50a
7885ede
d0e1a88
0d93f35
0564c73
801f888
c875ca5
9b258b2
aeb85b7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -220,16 +220,43 @@ HRESULT CLRPrivBinderAssemblyLoadContext::FindAssemblyBySpec( | |
return E_FAIL; | ||
} | ||
|
||
#ifdef FEATURE_COLLECTIBLE_ALC | ||
|
||
HRESULT CLRPrivBinderAssemblyLoadContext::GetIsCollectible(BOOL *pIsCollectible) | ||
{ | ||
*pIsCollectible = m_isCollectible; | ||
return S_OK; | ||
} | ||
|
||
HRESULT CLRPrivBinderAssemblyLoadContext::ReferenceLoaderAllocator(LoaderAllocator *pLoaderAllocator) | ||
{ | ||
CrstHolder holder(m_loadersCrst); | ||
|
||
// The same LoaderAllocator should not be used twice | ||
_ASSERTE(m_loaderAllocators.Lookup(pLoaderAllocator) == NULL); | ||
|
||
VERIFY(pLoaderAllocator->AddReferenceIfAlive()); | ||
|
||
m_loaderAllocators.Add(pLoaderAllocator); | ||
|
||
return S_OK; | ||
} | ||
|
||
#endif // FEATURE_COLLECTIBLE_ALC | ||
|
||
//============================================================================= | ||
// Creates an instance of the AssemblyLoadContext Binder | ||
// | ||
// This method does not take a lock since it is invoked from the ctor of the | ||
// managed AssemblyLoadContext type. | ||
//============================================================================= | ||
/* static */ | ||
HRESULT CLRPrivBinderAssemblyLoadContext::SetupContext(DWORD dwAppDomainId, | ||
HRESULT CLRPrivBinderAssemblyLoadContext::SetupContext(AppDomain *pAppDomain, | ||
CLRPrivBinderCoreCLR *pTPABinder, | ||
UINT_PTR ptrAssemblyLoadContext, | ||
UINT_PTR ptrAssemblyLoadContext, | ||
#ifdef FEATURE_COLLECTIBLE_ALC | ||
BOOL fIsCollectible, | ||
#endif // FEATURE_COLLECTIBLE_ALC | ||
CLRPrivBinderAssemblyLoadContext **ppBindContext) | ||
{ | ||
HRESULT hr = E_FAIL; | ||
|
@@ -244,7 +271,7 @@ HRESULT CLRPrivBinderAssemblyLoadContext::SetupContext(DWORD dwAppDomainId, | |
if(SUCCEEDED(hr)) | ||
{ | ||
// Save the reference to the AppDomain in which the binder lives | ||
pBinder->m_appContext.SetAppDomainId(dwAppDomainId); | ||
pBinder->m_appContext.SetAppDomainId(pAppDomain->GetId().m_dwId); | ||
|
||
// Mark that this binder can explicitly bind to native images | ||
pBinder->m_appContext.SetExplicitBindToNativeImages(true); | ||
|
@@ -257,6 +284,10 @@ HRESULT CLRPrivBinderAssemblyLoadContext::SetupContext(DWORD dwAppDomainId, | |
// AssemblyLoadContext instance | ||
pBinder->m_ptrManagedAssemblyLoadContext = ptrAssemblyLoadContext; | ||
|
||
#ifdef FEATURE_COLLECTIBLE_ALC | ||
pBinder->m_isCollectible = fIsCollectible; | ||
#endif // FEATURE_COLLECTIBLE_ALC | ||
|
||
// Return reference to the allocated Binder instance | ||
*ppBindContext = clr::SafeAddRef(pBinder.Extract()); | ||
} | ||
|
@@ -268,9 +299,80 @@ HRESULT CLRPrivBinderAssemblyLoadContext::SetupContext(DWORD dwAppDomainId, | |
return hr; | ||
} | ||
|
||
#ifdef FEATURE_COLLECTIBLE_ALC | ||
|
||
/* static */ | ||
BOOL CLRPrivBinderAssemblyLoadContext::DestroyContext(CLRPrivBinderAssemblyLoadContext *pBindContext) | ||
{ | ||
{ | ||
CrstHolder holder(pBindContext->m_loadersCrst); | ||
|
||
LoaderAllocatorSet &loaderAllocators = pBindContext->m_loaderAllocators; | ||
|
||
LoaderAllocatorSet::Iterator iter = loaderAllocators.Begin(); | ||
while (iter != loaderAllocators.End()) | ||
{ | ||
LoaderAllocator *pLoaderAllocator = *iter; | ||
|
||
// This context should be holding onto a reference to the LoaderAllocator, so it | ||
// should still be alive | ||
_ASSERTE(pLoaderAllocator->IsAlive()); | ||
|
||
if (pLoaderAllocator->IsManagedScoutAlive()) | ||
{ | ||
// We can't destroy until there is no managed reference to any of our LoaderAllocators | ||
return FALSE; | ||
} | ||
|
||
iter++; | ||
} | ||
|
||
iter = loaderAllocators.Begin(); | ||
while (iter != loaderAllocators.End()) | ||
{ | ||
LoaderAllocator *pLoaderAllocator = *iter; | ||
|
||
pLoaderAllocator->ReleaseAllReferences(); | ||
|
||
// Release our reference to the LoaderAllocator so it can be deleted by the LoaderAllocator GC | ||
VERIFY(pLoaderAllocator->Release()); | ||
|
||
iter++; | ||
} | ||
} | ||
|
||
// TODO: Is AppDomain::GetCurrentDomain() going to work properly? | ||
AppDomain *pAppDomain = AppDomain::GetCurrentDomain(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The above will work only when there is a single AppDomain in the process (which happens to be the current design for CoreCLR). What you really want is to get reference to the AppDomain that is associated with the Binder. CLRPrivBinderAssemblyLoadContext::SetupContext associates the Binder's AppContext with the AppDomain who assemblies it is managing. Use SystemDomain::GetAppDomainFromId to get the AppDomain* for AppDomainID saved in SetupContext. |
||
|
||
// The managed LoaderAllocatorScout finalizer would normally trigger the LoaderAllocator GC | ||
// but because we hold a reference that outlasts it, we need to trigger it as well | ||
LoaderAllocator::GCLoaderAllocators(pAppDomain); | ||
|
||
// The managed AssemblyLoadContext should be the only remaining reference to the native | ||
// binding context | ||
VERIFY(pBindContext->Release() == 0); | ||
|
||
return TRUE; | ||
} | ||
|
||
#endif // FEATURE_COLLECTIBLE_ALC | ||
|
||
CLRPrivBinderAssemblyLoadContext::CLRPrivBinderAssemblyLoadContext() | ||
{ | ||
m_pTPABinder = NULL; | ||
|
||
#ifdef FEATURE_COLLECTIBLE_ALC | ||
m_isCollectible = FALSE; | ||
|
||
m_loadersCrst = new Crst(CrstType::CrstAssemblyLoadContextLoaderAllocators); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use "new (nothrow)" here. And as a consequence, you will need to fixup the uses as well accordingly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure how to do that, Crst overrides new which is preventing me from using nothrow. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider using CrstStatic instead and use its InitNoThrow functionality. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What should I do if it fails to initialize? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Collectible ALC support should remain disabled at runtime. Any attempt to create a collectible ALC should raise an exception. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How do I use the CrstStatic class in clrprivbinderassemblyloadcontext.h? Including crst.h gives me lots of errors like: Also how/what exception should I throw? Should it be something like this in if (!pBinder->m_loadersCrst.InitNoThrow(CrstType::CrstAssemblyLoadContextLoaderAllocators))
GO_WITH_HRESULT(E_OUTOFMEMORY); |
||
#endif // FEATURE_COLLECTIBLE_ALC | ||
} | ||
|
||
CLRPrivBinderAssemblyLoadContext::~CLRPrivBinderAssemblyLoadContext() | ||
{ | ||
#ifdef FEATURE_COLLECTIBLE_ALC | ||
delete m_loadersCrst; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check for null prior to delete. |
||
#endif | ||
} | ||
|
||
#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,32 @@ namespace BINDER_SPACE | |
class AssemblyIdentityUTF8; | ||
}; | ||
|
||
class CLRPrivBinderAssemblyLoadContext : public IUnknownCommon<ICLRPrivBinder> | ||
class AppDomain; | ||
|
||
#ifdef FEATURE_COLLECTIBLE_ALC | ||
|
||
class Object; | ||
class Assembly; | ||
class LoaderAllocator; | ||
|
||
class DECLSPEC_UUID("68220E65-3D3F-42E2-BAD6-2D07419DAB5E") IAssemblyLoadContext : public IUnknown | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would suggest renaming the interface to be more incontext with its purpose - something like "ICollectibleAssemblyLoadContext". |
||
{ | ||
public: | ||
STDMETHOD(GetIsCollectible)( | ||
/* [retval][out] */ BOOL *pIsCollectible) = 0; | ||
|
||
STDMETHOD(ReferenceLoaderAllocator)( | ||
/* [in] */ LoaderAllocator *pLoaderAllocator) = 0; | ||
}; | ||
|
||
#endif // FEATURE_COLLECTIBLE_ALC | ||
|
||
class CLRPrivBinderAssemblyLoadContext : | ||
#ifndef FEATURE_COLLECTIBLE_ALC | ||
public IUnknownCommon<ICLRPrivBinder> | ||
#else // !FEATURE_COLLECTIBLE_ALC | ||
public IUnknownCommon<ICLRPrivBinder, IAssemblyLoadContext> | ||
#endif // FEATURE_COLLECTIBLE_ALC | ||
{ | ||
public: | ||
|
||
|
@@ -46,15 +71,37 @@ class CLRPrivBinderAssemblyLoadContext : public IUnknownCommon<ICLRPrivBinder> | |
/* [out] */ HRESULT *pResult, | ||
/* [out] */ ICLRPrivAssembly **ppAssembly); | ||
|
||
#ifdef FEATURE_COLLECTIBLE_ALC | ||
|
||
//========================================================================= | ||
// IAssemblyLoadContext functions | ||
//------------------------------------------------------------------------- | ||
STDMETHOD(GetIsCollectible)( | ||
/* [retval][out] */ BOOL *pIsCollectible); | ||
|
||
STDMETHOD(ReferenceLoaderAllocator)( | ||
/* [in] */ LoaderAllocator *pLoaderAllocator); | ||
|
||
#endif // FEATURE_COLLECTIBLE_ALC | ||
|
||
public: | ||
//========================================================================= | ||
// Class functions | ||
//------------------------------------------------------------------------- | ||
|
||
static HRESULT SetupContext(DWORD dwAppDomainId, CLRPrivBinderCoreCLR *pTPABinder, | ||
UINT_PTR ptrAssemblyLoadContext, CLRPrivBinderAssemblyLoadContext **ppBindContext); | ||
|
||
static HRESULT SetupContext(AppDomain *pAppDomain, CLRPrivBinderCoreCLR *pTPABinder, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned in the other comment, it is not clear why AppDomain* is being passed as opposed to the ID. |
||
UINT_PTR ptrAssemblyLoadContext, | ||
#ifdef FEATURE_COLLECTIBLE_ALC | ||
BOOL fIsCollectible, | ||
#endif // FEATURE_COLLECTIBLE_ALC | ||
CLRPrivBinderAssemblyLoadContext **ppBindContext); | ||
|
||
#ifdef FEATURE_COLLECTIBLE_ALC | ||
static BOOL DestroyContext(CLRPrivBinderAssemblyLoadContext *pBindContext); | ||
#endif // FEATURE_COLLECTIBLE_ALC | ||
|
||
CLRPrivBinderAssemblyLoadContext(); | ||
~CLRPrivBinderAssemblyLoadContext(); | ||
|
||
inline BINDER_SPACE::ApplicationContext *GetAppContext() | ||
{ | ||
|
@@ -81,6 +128,14 @@ class CLRPrivBinderAssemblyLoadContext : public IUnknownCommon<ICLRPrivBinder> | |
CLRPrivBinderCoreCLR *m_pTPABinder; | ||
|
||
INT_PTR m_ptrManagedAssemblyLoadContext; | ||
|
||
#ifdef FEATURE_COLLECTIBLE_ALC | ||
BOOL m_isCollectible; | ||
|
||
class Crst *m_loadersCrst; | ||
typedef SHash<PtrSetSHashTraits<LoaderAllocator * > > LoaderAllocatorSet; | ||
LoaderAllocatorSet m_loaderAllocators; | ||
#endif // FEATURE_COLLECTIBLE_ALC | ||
}; | ||
|
||
#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a specific reason we need to pass AppDomain* to get the ID, instead of just passing the ID?