diff options
59 files changed, 1242 insertions, 419 deletions
diff --git a/clr.coreclr.props b/clr.coreclr.props index e4f2483f95..872cbbca83 100644 --- a/clr.coreclr.props +++ b/clr.coreclr.props @@ -1,5 +1,6 @@ <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> + <FeatureCollectibleALC>true</FeatureCollectibleALC> <FeatureEventTrace>true</FeatureEventTrace> <FeatureHardwareIntrinsics>true</FeatureHardwareIntrinsics> <FeatureICastable>true</FeatureICastable> @@ -34,4 +35,4 @@ <FeatureWin32Registry>true</FeatureWin32Registry> <FeatureProfAttach>true</FeatureProfAttach> </PropertyGroup> -</Project>
\ No newline at end of file +</Project> diff --git a/clr.defines.targets b/clr.defines.targets index 04bfbda7fc..32876ea3b7 100644 --- a/clr.defines.targets +++ b/clr.defines.targets @@ -1,5 +1,4 @@ <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <!-- Features we're currently flighting, but don't intend to ship in officially supported releases --> <PropertyGroup Condition="'$(IsPrerelease)' == 'true'"> <FeatureDefaultInterfaces>true</FeatureDefaultInterfaces> @@ -11,6 +10,7 @@ <DefineConstants Condition="'$(FeatureMulticastStubAsIL)' == 'true'">$(DefineConstants);FEATURE_MULTICASTSTUB_AS_IL</DefineConstants> <DefineConstants Condition="'$(FeatureStubsAsIL)' == 'true'">$(DefineConstants);FEATURE_STUBS_AS_IL</DefineConstants> <DefineConstants Condition="'$(FeatureClassicCominterop)' == 'true'">$(DefineConstants);FEATURE_CLASSIC_COMINTEROP</DefineConstants> + <DefineConstants Condition="'$(FeatureCollectibleALC)' == 'true'">$(DefineConstants);FEATURE_COLLECTIBLE_ALC</DefineConstants> <DefineConstants Condition="'$(FeatureCominterop)' == 'true'">$(DefineConstants);FEATURE_COMINTEROP</DefineConstants> <DefineConstants Condition="'$(FeatureCominteropApartmentSupport)' == 'true'">$(DefineConstants);FEATURE_COMINTEROP_APARTMENT_SUPPORT</DefineConstants> <DefineConstants Condition="'$(FeatureCominteropUnmanagedActivation)' == 'true'">$(DefineConstants);FEATURE_COMINTEROP_UNMANAGED_ACTIVATION</DefineConstants> diff --git a/src/System.Private.CoreLib/Resources/Strings.resx b/src/System.Private.CoreLib/Resources/Strings.resx index ec250f37d2..0a3b1b588a 100644 --- a/src/System.Private.CoreLib/Resources/Strings.resx +++ b/src/System.Private.CoreLib/Resources/Strings.resx @@ -1912,6 +1912,18 @@ <data name="ArrayTypeMismatch_ConstrainedCopy" xml:space="preserve"> <value>Array.ConstrainedCopy will only work on array types that are provably compatible, without any form of boxing, unboxing, widening, or casting of each array element. Change the array types (i.e., copy a Derived[] to a Base[]), or use a mitigation strategy in the CER for Array.Copy's less powerful reliability contract, such as cloning the array or throwing away the potentially corrupt destination array.</value> </data> + <data name="AssemblyLoadContext_Constructor_CannotInstantiateWhileUnloading" xml:space="preserve"> + <value>Cannot instantiate AssemblyLoadContext while the current process is exiting.</value> + </data> + <data name="AssemblyLoadContext_Unload_CannotUnloadIfNotCollectible" xml:space="preserve"> + <value>Cannot unload non-collectible AssemblyLoadContext.</value> + </data> + <data name="AssemblyLoadContext_Unload_AlreadyUnloaded" xml:space="preserve"> + <value>Unload called on AssemblyLoadContext that is unloading or that was already unloaded.</value> + </data> + <data name="AssemblyLoadContext_Verify_NotUnloading" xml:space="preserve"> + <value>AssemblyLoadContext is unloading or was already unloaded.</value> + </data> <data name="AssertionFailed" xml:space="preserve"> <value>Assertion failed.</value> </data> diff --git a/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs b/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs index addd8f8516..746d39742b 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs @@ -4,6 +4,8 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Reflection; using System.IO; using System.Runtime.Versioning; @@ -17,11 +19,27 @@ namespace System.Runtime.Loader { public abstract class AssemblyLoadContext { + private static readonly Dictionary<long, WeakReference<AssemblyLoadContext>> ContextsToUnload = new Dictionary<long, WeakReference<AssemblyLoadContext>>(); + private static long _nextId; + private static bool _isProcessExiting; + + // Id used by contextsToUnload + private readonly long id; + + // synchronization primitive to protect against usage of this instance while unloading + private readonly object unloadLock = new object(); + + // Indicates the state of this ALC (Alive or in Unloading state) + private InternalState state; + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] private static extern bool CanUseAppPathAssemblyLoadContextInCurrentDomain(); [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - private static extern IntPtr InitializeAssemblyLoadContext(IntPtr ptrAssemblyLoadContext, bool fRepresentsTPALoadContext); + private static extern IntPtr InitializeAssemblyLoadContext(IntPtr ptrAssemblyLoadContext, bool fRepresentsTPALoadContext, bool isCollectible); + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + private static extern void PrepareForAssemblyLoadContextRelease(IntPtr ptrNativeAssemblyLoadContext, IntPtr ptrAssemblyLoadContextStrong); [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] private static extern IntPtr LoadFromStream(IntPtr ptrNativeAssemblyLoadContext, IntPtr ptrAssemblyArray, int iAssemblyArrayLen, IntPtr ptrSymbols, int iSymbolArrayLen, ObjectHandleOnStack retAssembly); @@ -32,37 +50,92 @@ namespace System.Runtime.Loader [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] internal static extern void InternalStartProfile(string profile, IntPtr ptrNativeAssemblyLoadContext); - protected AssemblyLoadContext() + static AssemblyLoadContext() { - // Initialize the ALC representing non-TPA LoadContext - InitializeLoadContext(false); + // We register the cleanup of all AssemblyLoadContext that have not been finalized in the AppContext.Unloading + AppContext.Unloading += OnAppContextUnloading; } - internal AssemblyLoadContext(bool fRepresentsTPALoadContext) + protected AssemblyLoadContext() : this(false, false) { - // Initialize the ALC representing TPA LoadContext - InitializeLoadContext(fRepresentsTPALoadContext); } - private void InitializeLoadContext(bool fRepresentsTPALoadContext) + protected AssemblyLoadContext(bool isCollectible) : this(false, isCollectible) + { + } + + internal AssemblyLoadContext(bool fRepresentsTPALoadContext, bool isCollectible) { // Initialize the VM side of AssemblyLoadContext if not already done. - GCHandle gchALC = GCHandle.Alloc(this); - IntPtr ptrALC = GCHandle.ToIntPtr(gchALC); - m_pNativeAssemblyLoadContext = InitializeAssemblyLoadContext(ptrALC, fRepresentsTPALoadContext); + IsCollectible = isCollectible; + + // Add this instance to the list of alive ALC + lock (ContextsToUnload) + { + if (_isProcessExiting) + { + throw new InvalidOperationException(SR.GetResourceString("AssemblyLoadContext_Constructor_CannotInstantiateWhileUnloading")); + } + + // If this is a collectible ALC, we are creating a weak handle otherwise we use a strong handle + var thisHandle = GCHandle.Alloc(this, IsCollectible ? GCHandleType.Weak : GCHandleType.Normal); + var thisHandlePtr = GCHandle.ToIntPtr(thisHandle); + m_pNativeAssemblyLoadContext = InitializeAssemblyLoadContext(thisHandlePtr, fRepresentsTPALoadContext, isCollectible); + + // Initialize event handlers to be null by default + Resolving = null; + Unloading = null; + + id = _nextId++; + ContextsToUnload.Add(id, new WeakReference<AssemblyLoadContext>(this, true)); + } + } + + ~AssemblyLoadContext() + { + // Only valid for a Collectible ALC. Non-collectible ALCs have the finalizer suppressed. + // We get here only in case the explicit Unload was not initiated. + Debug.Assert(state != InternalState.Unloading); + InitiateUnload(); + } - // Initialize event handlers to be null by default - Resolving = null; + private void InitiateUnload() + { + var unloading = Unloading; Unloading = null; + unloading?.Invoke(this); - // Since unloading an AssemblyLoadContext is not yet implemented, this is a temporary solution to raise the - // Unloading event on process exit. Register for the current AppDomain's ProcessExit event, and the handler will in - // turn raise the Unloading event. - AppContext.Unloading += OnAppContextUnloading; + // When in Unloading state, we are not supposed to be called on the finalizer + // as the native side is holding a strong reference after calling Unload + lock (unloadLock) + { + if (!_isProcessExiting) + { + Debug.Assert(state == InternalState.Alive); + + var thisStrongHandle = GCHandle.Alloc(this, GCHandleType.Normal); + var thisStrongHandlePtr = GCHandle.ToIntPtr(thisStrongHandle); + // The underlying code will transform the original weak handle + // created by InitializeLoadContext to a strong handle + PrepareForAssemblyLoadContextRelease(m_pNativeAssemblyLoadContext, thisStrongHandlePtr); + } + + state = InternalState.Unloading; + } + + if (!_isProcessExiting) + { + lock (ContextsToUnload) + { + ContextsToUnload.Remove(id); + } + } } + public bool IsCollectible { get; } + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - private static extern void LoadFromPath(IntPtr ptrNativeAssemblyLoadContext, string ilPath, string niPath, ObjectHandleOnStack retAssembly); + private static extern void LoadFromPath(IntPtr ptrNativeAssemblyLoadContet, string ilPath, string niPath, ObjectHandleOnStack retAssembly); public static Assembly[] GetLoadedAssemblies() { @@ -78,14 +151,19 @@ namespace System.Runtime.Loader throw new ArgumentNullException(nameof(assemblyPath)); } - if (PathInternal.IsPartiallyQualified(assemblyPath)) + lock (unloadLock) { - throw new ArgumentException(SR.Argument_AbsolutePathRequired, nameof(assemblyPath)); - } + VerifyIsAlive(); + if (PathInternal.IsPartiallyQualified(assemblyPath)) + { + throw new ArgumentException(SR.GetResourceString("Argument_AbsolutePathRequired"), + nameof(assemblyPath)); + } - RuntimeAssembly loadedAssembly = null; - LoadFromPath(m_pNativeAssemblyLoadContext, assemblyPath, null, JitHelpers.GetObjectHandleOnStack(ref loadedAssembly)); - return loadedAssembly; + RuntimeAssembly loadedAssembly = null; + LoadFromPath(m_pNativeAssemblyLoadContext, assemblyPath, null, JitHelpers.GetObjectHandleOnStack(ref loadedAssembly)); + return loadedAssembly; + } } public Assembly LoadFromNativeImagePath(string nativeImagePath, string assemblyPath) @@ -95,21 +173,28 @@ namespace System.Runtime.Loader throw new ArgumentNullException(nameof(nativeImagePath)); } - if (PathInternal.IsPartiallyQualified(nativeImagePath)) + lock (unloadLock) { - throw new ArgumentException(SR.Argument_AbsolutePathRequired, nameof(nativeImagePath)); - } + VerifyIsAlive(); - if (assemblyPath != null && PathInternal.IsPartiallyQualified(assemblyPath)) - { - throw new ArgumentException(SR.Argument_AbsolutePathRequired, nameof(assemblyPath)); - } + if (PathInternal.IsPartiallyQualified(nativeImagePath)) + { + throw new ArgumentException(SR.GetResourceString("Argument_AbsolutePathRequired"), + nameof(nativeImagePath)); + } - // Basic validation has succeeded - lets try to load the NI image. - // Ask LoadFile to load the specified assembly in the DefaultContext - RuntimeAssembly loadedAssembly = null; - LoadFromPath(m_pNativeAssemblyLoadContext, assemblyPath, nativeImagePath, JitHelpers.GetObjectHandleOnStack(ref loadedAssembly)); - return loadedAssembly; + if (assemblyPath != null && PathInternal.IsPartiallyQualified(assemblyPath)) + { + throw new ArgumentException(SR.GetResourceString("Argument_AbsolutePathRequired"), + nameof(assemblyPath)); + } + + // Basic validation has succeeded - lets try to load the NI image. + // Ask LoadFile to load the specified assembly in the DefaultContext + RuntimeAssembly loadedAssembly = null; + LoadFromPath(m_pNativeAssemblyLoadContext, assemblyPath, nativeImagePath, JitHelpers.GetObjectHandleOnStack(ref loadedAssembly)); + return loadedAssembly; + } } public Assembly LoadFromStream(Stream assembly) @@ -128,36 +213,59 @@ namespace System.Runtime.Loader { throw new BadImageFormatException(SR.BadImageFormat_BadILFormat); } + lock (unloadLock) + { + VerifyIsAlive(); - int iAssemblyStreamLength = (int)assembly.Length; - int iSymbolLength = 0; + int iAssemblyStreamLength = (int) assembly.Length; + int iSymbolLength = 0; - // Allocate the byte[] to hold the assembly - byte[] arrAssembly = new byte[iAssemblyStreamLength]; + // Allocate the byte[] to hold the assembly + byte[] arrAssembly = new byte[iAssemblyStreamLength]; - // Copy the assembly to the byte array - assembly.Read(arrAssembly, 0, iAssemblyStreamLength); + // Copy the assembly to the byte array + assembly.Read(arrAssembly, 0, iAssemblyStreamLength); - // Get the symbol stream in byte[] if provided - byte[] arrSymbols = null; - if (assemblySymbols != null) - { - iSymbolLength = (int)assemblySymbols.Length; - arrSymbols = new byte[iSymbolLength]; + // Get the symbol stream in byte[] if provided + byte[] arrSymbols = null; + if (assemblySymbols != null) + { + iSymbolLength = (int) assemblySymbols.Length; + arrSymbols = new byte[iSymbolLength]; - assemblySymbols.Read(arrSymbols, 0, iSymbolLength); - } + assemblySymbols.Read(arrSymbols, 0, iSymbolLength); + } - RuntimeAssembly loadedAssembly = null; - unsafe - { - fixed (byte* ptrAssembly = arrAssembly, ptrSymbols = arrSymbols) + RuntimeAssembly loadedAssembly = null; + unsafe { - LoadFromStream(m_pNativeAssemblyLoadContext, new IntPtr(ptrAssembly), iAssemblyStreamLength, new IntPtr(ptrSymbols), iSymbolLength, JitHelpers.GetObjectHandleOnStack(ref loadedAssembly)); + fixed (byte* ptrAssembly = arrAssembly, ptrSymbols = arrSymbols) + { + LoadFromStream(m_pNativeAssemblyLoadContext, new IntPtr(ptrAssembly), iAssemblyStreamLength, + new IntPtr(ptrSymbols), iSymbolLength, JitHelpers.GetObjectHandleOnStack(ref loadedAssembly)); + } } + return loadedAssembly; } + } - return loadedAssembly; + public void Unload() + { + if (!IsCollectible) + { + throw new InvalidOperationException(SR.GetResourceString("AssemblyLoadContext_Unload_CannotUnloadIfNotCollectible")); + } + + GC.SuppressFinalize(this); + InitiateUnload(); + } + + private void VerifyIsAlive() + { + if (state != InternalState.Alive) + { + throw new InvalidOperationException(SR.GetResourceString("AssemblyLoadContext_Verify_NotUnloading")); + } } // Custom AssemblyLoadContext implementations can override this @@ -391,12 +499,26 @@ namespace System.Runtime.Loader InternalStartProfile(profile, m_pNativeAssemblyLoadContext); } - private void OnAppContextUnloading(object sender, EventArgs e) + private void OnAppContextUnloading() { - var unloading = Unloading; - if (unloading != null) + InitiateUnload(); + } + + private static void OnAppContextUnloading(object sender, EventArgs e) + { + lock (ContextsToUnload) { - unloading(this); + _isProcessExiting = true; + foreach (var alcAlive in ContextsToUnload) + { + AssemblyLoadContext alc; + if (alcAlive.Value.TryGetTarget(out alc)) + { + // Should we use a try/catch? + alc.OnAppContextUnloading(); + } + } + ContextsToUnload.Clear(); } } @@ -442,11 +564,25 @@ namespace System.Runtime.Loader add { AppDomain.CurrentDomain.AssemblyResolve += value; } remove { AppDomain.CurrentDomain.AssemblyResolve -= value; } } + + private enum InternalState + { + /// <summary> + /// The ALC is alive (default) + /// </summary> + Alive, + + /// <summary> + /// The unload process has started, the Unloading event will be called + /// once the underlying LoaderAllocator has been finalized + /// </summary> + Unloading + } } internal class AppPathAssemblyLoadContext : AssemblyLoadContext { - internal AppPathAssemblyLoadContext() : base(true) + internal AppPathAssemblyLoadContext() : base(true, false) { } @@ -460,7 +596,7 @@ namespace System.Runtime.Loader internal class IndividualAssemblyLoadContext : AssemblyLoadContext { - internal IndividualAssemblyLoadContext() : base(false) + internal IndividualAssemblyLoadContext() : base(false, false) { } @@ -469,5 +605,6 @@ namespace System.Runtime.Loader return null; } } + } diff --git a/src/binder/assembly.cpp b/src/binder/assembly.cpp index 5d3a674a55..8faa00a908 100644 --- a/src/binder/assembly.cpp +++ b/src/binder/assembly.cpp @@ -326,6 +326,11 @@ Exit: return (m_pBinder == NULL) ? E_FAIL : m_pBinder->GetBinderFlags(pBinderFlags); } + HRESULT Assembly::GetLoaderAllocator(LPVOID* pLoaderAllocator) + { + return (m_pBinder == NULL) ? E_FAIL : m_pBinder->GetLoaderAllocator(pLoaderAllocator); + } + HRESULT Assembly::IsShareable( BOOL * pbIsShareable) { diff --git a/src/binder/clrprivbinderassemblyloadcontext.cpp b/src/binder/clrprivbinderassemblyloadcontext.cpp index e6f957aabe..e1ecdc8436 100644 --- a/src/binder/clrprivbinderassemblyloadcontext.cpp +++ b/src/binder/clrprivbinderassemblyloadcontext.cpp @@ -203,6 +203,18 @@ HRESULT CLRPrivBinderAssemblyLoadContext::FindAssemblyBySpec( return E_FAIL; } +HRESULT CLRPrivBinderAssemblyLoadContext::GetLoaderAllocator(LPVOID* pLoaderAllocator) +{ + _ASSERTE(pLoaderAllocator != NULL); + if (m_pAssemblyLoaderAllocator == NULL) + { + return E_FAIL; + } + + *pLoaderAllocator = m_pAssemblyLoaderAllocator; + return S_OK; +} + //============================================================================= // Creates an instance of the AssemblyLoadContext Binder // @@ -212,7 +224,9 @@ HRESULT CLRPrivBinderAssemblyLoadContext::FindAssemblyBySpec( /* static */ HRESULT CLRPrivBinderAssemblyLoadContext::SetupContext(DWORD dwAppDomainId, CLRPrivBinderCoreCLR *pTPABinder, - UINT_PTR ptrAssemblyLoadContext, + LoaderAllocator* pLoaderAllocator, + void* loaderAllocatorHandle, + UINT_PTR ptrAssemblyLoadContext, CLRPrivBinderAssemblyLoadContext **ppBindContext) { HRESULT hr = E_FAIL; @@ -240,6 +254,20 @@ HRESULT CLRPrivBinderAssemblyLoadContext::SetupContext(DWORD dwAppDomainId, // AssemblyLoadContext instance pBinder->m_ptrManagedAssemblyLoadContext = ptrAssemblyLoadContext; + if (pLoaderAllocator != NULL) + { + // Link to LoaderAllocator, keep a reference to it + VERIFY(pLoaderAllocator->AddReferenceIfAlive()); + } + pBinder->m_pAssemblyLoaderAllocator = pLoaderAllocator; + pBinder->m_loaderAllocatorHandle = loaderAllocatorHandle; + +#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + if (pLoaderAllocator != NULL) + { + ((AssemblyLoaderAllocator*)pLoaderAllocator)->RegisterBinder(pBinder); + } +#endif // Return reference to the allocated Binder instance *ppBindContext = clr::SafeAddRef(pBinder.Extract()); } @@ -251,9 +279,53 @@ Exit: return hr; } +void CLRPrivBinderAssemblyLoadContext::PrepareForLoadContextRelease(INT_PTR ptrManagedStrongAssemblyLoadContext) +{ + CONTRACTL + { + GC_NOTRIGGER; + THROWS; + MODE_COOPERATIVE; + SO_TOLERANT; + } + CONTRACTL_END; + + // Replace the weak handle with a strong handle so that the managed assembly load context stays alive until the + // CLRPrivBinderAssemblyLoadContext::ReleaseLoadContext is called. + OBJECTHANDLE handle = reinterpret_cast<OBJECTHANDLE>(m_ptrManagedAssemblyLoadContext); + OBJECTHANDLE strongHandle = reinterpret_cast<OBJECTHANDLE>(ptrManagedStrongAssemblyLoadContext); + DestroyShortWeakHandle(handle); + m_ptrManagedAssemblyLoadContext = reinterpret_cast<INT_PTR>(strongHandle); + + _ASSERTE(m_pAssemblyLoaderAllocator != NULL); + _ASSERTE(m_loaderAllocatorHandle != NULL); + + // We cannot delete the binder here as it is used indirectly when comparing assemblies with the same binder + // It will be deleted when the LoaderAllocator will be deleted + // But we can release the LoaderAllocator as we are no longer using it here + m_pAssemblyLoaderAllocator->Release(); + m_pAssemblyLoaderAllocator = NULL; + + // Destroy the strong handle to the LoaderAllocator in order to let it reach its finalizer + DestroyHandle(reinterpret_cast<OBJECTHANDLE>(m_loaderAllocatorHandle)); + m_loaderAllocatorHandle = NULL; +} + CLRPrivBinderAssemblyLoadContext::CLRPrivBinderAssemblyLoadContext() { m_pTPABinder = NULL; } +void CLRPrivBinderAssemblyLoadContext::ReleaseLoadContext() +{ + VERIFY(m_ptrManagedAssemblyLoadContext != NULL); + + // This method is called to release the strong handle on the managed AssemblyLoadContext + // once the Unloading event has been fired + OBJECTHANDLE handle = reinterpret_cast<OBJECTHANDLE>(m_ptrManagedAssemblyLoadContext); + DestroyHandle(handle); + m_ptrManagedAssemblyLoadContext = NULL; +} + #endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + diff --git a/src/binder/clrprivbindercoreclr.cpp b/src/binder/clrprivbindercoreclr.cpp index d756454edb..051389cb6f 100644 --- a/src/binder/clrprivbindercoreclr.cpp +++ b/src/binder/clrprivbindercoreclr.cpp @@ -292,6 +292,12 @@ Exit: return hr; } +HRESULT CLRPrivBinderCoreCLR::GetLoaderAllocator(LPVOID* pLoaderAllocator) +{ + // Not supported by this binder + return E_FAIL; +} + #ifndef CROSSGEN_COMPILE HRESULT CLRPrivBinderCoreCLR::PreBindByteArray(PEImage *pPEImage, BOOL fInspectionOnly) { diff --git a/src/binder/inc/assembly.hpp b/src/binder/inc/assembly.hpp index 425e80567e..7071e2eaa0 100644 --- a/src/binder/inc/assembly.hpp +++ b/src/binder/inc/assembly.hpp @@ -100,6 +100,8 @@ namespace BINDER_SPACE STDMETHOD(GetBinderFlags)(DWORD *pBinderFlags); + STDMETHOD(GetLoaderAllocator)(LPVOID* pLoaderAllocator); + // -------------------------------------------------------------------- // Assembly methods // -------------------------------------------------------------------- @@ -137,6 +139,11 @@ namespace BINDER_SPACE static PEKIND GetSystemArchitecture(); static BOOL IsValidArchitecture(PEKIND kArchitecture); + inline ICLRPrivBinder* GetBinder() + { + return m_pBinder; + } + #ifndef CROSSGEN_COMPILE protected: #endif @@ -195,11 +202,6 @@ public: _ASSERTE(m_pBinder == NULL || m_pBinder == pBinder); m_pBinder = pBinder; } - - inline ICLRPrivBinder* GetBinder() - { - return m_pBinder; - } friend class ::CLRPrivBinderCoreCLR; diff --git a/src/binder/inc/clrprivbinderassemblyloadcontext.h b/src/binder/inc/clrprivbinderassemblyloadcontext.h index 9c772338d6..9f3159f694 100644 --- a/src/binder/inc/clrprivbinderassemblyloadcontext.h +++ b/src/binder/inc/clrprivbinderassemblyloadcontext.h @@ -17,7 +17,14 @@ namespace BINDER_SPACE class AssemblyIdentityUTF8; }; -class CLRPrivBinderAssemblyLoadContext : public IUnknownCommon<ICLRPrivBinder> +class AppDomain; + +class Object; +class Assembly; +class LoaderAllocator; + +class CLRPrivBinderAssemblyLoadContext : + public IUnknownCommon<ICLRPrivBinder> { public: @@ -45,14 +52,24 @@ public: /* [out] */ HRESULT *pResult, /* [out] */ ICLRPrivAssembly **ppAssembly); + STDMETHOD(GetLoaderAllocator)( + /* [retval][out] */ LPVOID *pLoaderAllocator); + public: //========================================================================= // Class functions //------------------------------------------------------------------------- - static HRESULT SetupContext(DWORD dwAppDomainId, CLRPrivBinderCoreCLR *pTPABinder, - UINT_PTR ptrAssemblyLoadContext, CLRPrivBinderAssemblyLoadContext **ppBindContext); - + static HRESULT SetupContext(DWORD dwAppDomainId, + CLRPrivBinderCoreCLR *pTPABinder, + LoaderAllocator* pLoaderAllocator, + void* loaderAllocatorHandle, + UINT_PTR ptrAssemblyLoadContext, + CLRPrivBinderAssemblyLoadContext **ppBindContext); + + void PrepareForLoadContextRelease(INT_PTR ptrManagedStrongAssemblyLoadContext); + void ReleaseLoadContext(); + CLRPrivBinderAssemblyLoadContext(); inline BINDER_SPACE::ApplicationContext *GetAppContext() @@ -80,6 +97,9 @@ private: CLRPrivBinderCoreCLR *m_pTPABinder; INT_PTR m_ptrManagedAssemblyLoadContext; + + LoaderAllocator* m_pAssemblyLoaderAllocator; + void* m_loaderAllocatorHandle; }; #endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) diff --git a/src/binder/inc/clrprivbindercoreclr.h b/src/binder/inc/clrprivbindercoreclr.h index 9338fad3df..e0e82866b6 100644 --- a/src/binder/inc/clrprivbindercoreclr.h +++ b/src/binder/inc/clrprivbindercoreclr.h @@ -42,6 +42,9 @@ public: /* [out] */ HRESULT *pResult, /* [out] */ ICLRPrivAssembly **ppAssembly); + STDMETHOD(GetLoaderAllocator)( + /* [retval][out] */ LPVOID *pLoaderAllocator); + public: HRESULT SetupBindingPaths(SString &sTrustedPlatformAssemblies, diff --git a/src/dlls/mscorrc/mscorrc.rc b/src/dlls/mscorrc/mscorrc.rc index cf5b6b2b27..a298563cc6 100644 --- a/src/dlls/mscorrc/mscorrc.rc +++ b/src/dlls/mscorrc/mscorrc.rc @@ -726,7 +726,6 @@ BEGIN IDS_CLASSLOAD_STATICVIRTUAL "Method '%3' in type '%1' from assembly '%2' cannot be a static and a virtual." IDS_CLASSLOAD_REDUCEACCESS "Derived method '%3' in type '%1' from assembly '%2' cannot reduce access." IDS_CLASSLOAD_BADPINVOKE "Could not get PINVOKE information for method '%3' in type '%1' from assembly '%2'." - IDS_CLASSLOAD_COLLECTIBLEPINVOKE "PInvoke method not permitted on type '%1' from collectible assembly '%2'." IDS_CLASSLOAD_COLLECTIBLESPECIALSTATICS "Collectible type '%1' may not have Thread or Context static members." IDS_CLASSLOAD_VALUECLASSTOOLARGE "Array of type '%1' from assembly '%2' cannot be created because base value type is too large." IDS_CLASSLOAD_NOTIMPLEMENTED "Method '%3' in type '%1' from assembly '%2' does not have an implementation." diff --git a/src/dlls/mscorrc/resource.h b/src/dlls/mscorrc/resource.h index fb095fc291..d58780f8f9 100644 --- a/src/dlls/mscorrc/resource.h +++ b/src/dlls/mscorrc/resource.h @@ -233,7 +233,6 @@ #define IDS_CLASSLOAD_MI_MISSING_SIG_DECL 0x17a7 #define IDS_CLASSLOAD_TOOMANYGENERICARGS 0x17ab -#define IDS_CLASSLOAD_COLLECTIBLEPINVOKE 0x17ac #define IDS_CLASSLOAD_COLLECTIBLESPECIALSTATICS 0x17ad #define IDS_COMPLUS_ERROR 0x17ae #define IDS_FATAL_ERROR 0x17af diff --git a/src/inc/clrprivbinderutil.h b/src/inc/clrprivbinderutil.h index d42f2b8d66..fe922eeec8 100644 --- a/src/inc/clrprivbinderutil.h +++ b/src/inc/clrprivbinderutil.h @@ -154,6 +154,15 @@ namespace CLRPrivBinderUtil { STATIC_CONTRACT_WRAPPER; return _pWrapped->FindAssemblyBySpec(pvAppDomain, pvAssemblySpec, pResult, ppAssembly); } //----------------------------------------------------------------------------------------------------------------- + // Forwards to wrapped binder. + STDMETHOD(GetLoaderAllocator)( + LPVOID * pLoaderAllocator) + { + WRAPPER_NO_CONTRACT; + return _pWrapped->GetLoaderAllocator(pLoaderAllocator); + } + + //----------------------------------------------------------------------------------------------------------------- // ICLRPrivAssembly method is unsupported. STDMETHOD(IsShareable)( BOOL * pbIsShareable) diff --git a/src/inc/clrprivbinding.idl b/src/inc/clrprivbinding.idl index 5261f076d1..284bc96bf0 100644 --- a/src/inc/clrprivbinding.idl +++ b/src/inc/clrprivbinding.idl @@ -91,6 +91,16 @@ interface ICLRPrivBinder : IUnknown [in] LPVOID pvAssemblySpec, [out] HRESULT * pResult, [out] ICLRPrivAssembly ** ppAssembly); + + /********************************************************************************** + ** GetLoaderAllocator + ** Get LoaderAllocator for binders that contain it. For other binders, return + ** E_FAIL + ** + ** pLoaderAllocator - when successful, constains the returned LoaderAllocator + **********************************************************************************/ + HRESULT GetLoaderAllocator( + [out, retval] LPVOID * pLoaderAllocator); }; enum CLR_PRIV_BINDER_FLAGS diff --git a/src/inc/loaderheap.h b/src/inc/loaderheap.h index a2dec9cc25..ee45b74645 100644 --- a/src/inc/loaderheap.h +++ b/src/inc/loaderheap.h @@ -274,8 +274,8 @@ public: #endif public: - typedef void EnumPageRegionsCallback (PTR_VOID pvAllocationBase, SIZE_T cbReserved); - void EnumPageRegions (EnumPageRegionsCallback *pCallback); + typedef bool EnumPageRegionsCallback (PTR_VOID pvArgs, PTR_VOID pvAllocationBase, SIZE_T cbReserved); + void EnumPageRegions (EnumPageRegionsCallback *pCallback, PTR_VOID pvArgs); #ifndef DACCESS_COMPILE protected: diff --git a/src/pal/prebuilt/inc/clrprivbinding.h b/src/pal/prebuilt/inc/clrprivbinding.h index 4d3d36d05f..04ef10c3f6 100644 --- a/src/pal/prebuilt/inc/clrprivbinding.h +++ b/src/pal/prebuilt/inc/clrprivbinding.h @@ -171,7 +171,9 @@ EXTERN_C const IID IID_ICLRPrivBinder; /* [in] */ LPVOID pvAssemblySpec, /* [out] */ HRESULT *pResult, /* [out] */ ICLRPrivAssembly **ppAssembly) = 0; - + + virtual HRESULT STDMETHODCALLTYPE GetLoaderAllocator( + /* [retval][out] */ LPVOID* pLoaderAllocator) = 0; }; @@ -219,6 +221,10 @@ EXTERN_C const IID IID_ICLRPrivBinder; /* [out] */ HRESULT *pResult, /* [out] */ ICLRPrivAssembly **ppAssembly); + HRESULT(STDMETHODCALLTYPE *GetLoaderAllocator)( + ICLRPrivBinder * This, + /* [retval][out] */ LPVOID *pLoaderAllocator) = 0; + END_INTERFACE } ICLRPrivBinderVtbl; diff --git a/src/utilcode/loaderheap.cpp b/src/utilcode/loaderheap.cpp index 49f8a049f9..a2f8c4f276 100644 --- a/src/utilcode/loaderheap.cpp +++ b/src/utilcode/loaderheap.cpp @@ -1804,14 +1804,17 @@ void UnlockedLoaderHeap::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) #endif // #ifdef DACCESS_COMPILE -void UnlockedLoaderHeap::EnumPageRegions (EnumPageRegionsCallback *pCallback) +void UnlockedLoaderHeap::EnumPageRegions (EnumPageRegionsCallback *pCallback, PTR_VOID pvArgs) { WRAPPER_NO_CONTRACT; PTR_LoaderHeapBlock block = m_pFirstBlock; while (block) { - (*pCallback)(block->pVirtualAddress, block->dwVirtualSize); + if ((*pCallback)(pvArgs, block->pVirtualAddress, block->dwVirtualSize)) + { + break; + } block = block->pNext; } diff --git a/src/vm/appdomain.cpp b/src/vm/appdomain.cpp index 63d077e4ff..f1c7d0a81f 100644 --- a/src/vm/appdomain.cpp +++ b/src/vm/appdomain.cpp @@ -4834,7 +4834,7 @@ void AppDomain::AddAssembly(DomainAssembly * assem) } } -void AppDomain::RemoveAssembly_Unlocked(DomainAssembly * pAsm) +void AppDomain::RemoveAssembly(DomainAssembly * pAsm) { CONTRACTL { @@ -4843,8 +4843,7 @@ void AppDomain::RemoveAssembly_Unlocked(DomainAssembly * pAsm) } CONTRACTL_END; - _ASSERTE(GetAssemblyListLock()->OwnedByCurrentThread()); - + CrstHolder ch(GetAssemblyListLock()); DWORD asmCount = m_Assemblies.GetCount_Unlocked(); for (DWORD i = 0; i < asmCount; ++i) { @@ -5561,11 +5560,26 @@ DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity, if (result == NULL) { + LoaderAllocator *pLoaderAllocator = NULL; + +#ifndef CROSSGEN_COMPILE + ICLRPrivBinder *pFileBinder = pFile->GetBindingContext(); + if (pFileBinder != NULL) + { + // Assemblies loaded with AssemblyLoadContext need to use a different LoaderAllocator if + // marked as collectible + pFileBinder->GetLoaderAllocator((LPVOID*)&pLoaderAllocator); + } +#endif // !CROSSGEN_COMPILE + + if (pLoaderAllocator == NULL) + { + pLoaderAllocator = this->GetLoaderAllocator(); + } + // Allocate the DomainAssembly a bit early to avoid GC mode problems. We could potentially avoid // a rare redundant allocation by moving this closer to FileLoadLock::Create, but it's not worth it. - - NewHolder<DomainAssembly> pDomainAssembly; - pDomainAssembly = new DomainAssembly(this, pFile, this->GetLoaderAllocator()); + NewHolder<DomainAssembly> pDomainAssembly = new DomainAssembly(this, pFile, pLoaderAllocator); LoadLockHolder lock(this); @@ -5580,6 +5594,14 @@ DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity, // We are the first one in - create the DomainAssembly fileLock = FileLoadLock::Create(lock, pFile, pDomainAssembly); pDomainAssembly.SuppressRelease(); +#ifndef CROSSGEN_COMPILE + if (pDomainAssembly->IsCollectible()) + { + // We add the assembly to the LoaderAllocator only when we are sure that it can be added + // and won't be deleted in case of a concurrent load from the same ALC + ((AssemblyLoaderAllocator *)pLoaderAllocator)->AddDomainAssembly(pDomainAssembly); + } +#endif // !CROSSGEN_COMPILE } } else @@ -6003,17 +6025,15 @@ AppDomain::SharePolicy AppDomain::GetSharePolicy() #endif // FEATURE_LOADER_OPTIMIZATION -void AppDomain::CheckForMismatchedNativeImages(AssemblySpec * pSpec, const GUID * pGuid) +static void NormalizeAssemblySpecForNativeDependencies(AssemblySpec * pSpec) { - STANDARD_VM_CONTRACT; - - // - // The native images are ever used only for trusted images in CoreCLR. - // We don't wish to open the IL file at runtime so we just forgo any - // eager consistency checking. But we still want to prevent mistmatched - // NGen images from being used. We record all mappings between assembly - // names and MVID, and fail once we detect mismatch. - // + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; if (pSpec->IsStrongNamed() && pSpec->HasPublicKey()) { @@ -6031,7 +6051,21 @@ void AppDomain::CheckForMismatchedNativeImages(AssemblySpec * pSpec, const GUID pContext->usRevisionNumber = (USHORT)-1; // Ignore the WinRT type while considering if two assemblies have the same identity. - pSpec->SetWindowsRuntimeType(NULL, NULL); + pSpec->SetWindowsRuntimeType(NULL, NULL); +} + +void AppDomain::CheckForMismatchedNativeImages(AssemblySpec * pSpec, const GUID * pGuid) +{ + STANDARD_VM_CONTRACT; + + // + // The native images are ever used only for trusted images in CoreCLR. + // We don't wish to open the IL file at runtime so we just forgo any + // eager consistency checking. But we still want to prevent mistmatched + // NGen images from being used. We record all mappings between assembly + // names and MVID, and fail once we detect mismatch. + // + NormalizeAssemblySpecForNativeDependencies(pSpec); CrstHolder ch(&m_DomainCrst); @@ -6052,23 +6086,39 @@ void AppDomain::CheckForMismatchedNativeImages(AssemblySpec * pSpec, const GUID // // No entry yet - create one // - AllocMemTracker amTracker; - AllocMemTracker *pamTracker = &amTracker; - - NativeImageDependenciesEntry * pNewEntry = - new (pamTracker->Track(GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(NativeImageDependenciesEntry))))) - NativeImageDependenciesEntry(); - + NativeImageDependenciesEntry * pNewEntry = new NativeImageDependenciesEntry(); pNewEntry->m_AssemblySpec.CopyFrom(pSpec); - pNewEntry->m_AssemblySpec.CloneFieldsToLoaderHeap(AssemblySpec::ALL_OWNED, GetLowFrequencyHeap(), pamTracker); - + pNewEntry->m_AssemblySpec.CloneFields(AssemblySpec::ALL_OWNED); pNewEntry->m_guidMVID = *pGuid; - m_NativeImageDependencies.Add(pNewEntry); - amTracker.SuppressRelease(); } } +BOOL AppDomain::RemoveNativeImageDependency(AssemblySpec * pSpec) +{ + CONTRACTL + { + GC_NOTRIGGER; + PRECONDITION(CheckPointer(pSpec)); + } + CONTRACTL_END; + + BOOL result = FALSE; + NormalizeAssemblySpecForNativeDependencies(pSpec); + + CrstHolder ch(&m_DomainCrst); + + const NativeImageDependenciesEntry * pEntry = m_NativeImageDependencies.Lookup(pSpec); + + if (pEntry != NULL) + { + m_NativeImageDependencies.Remove(pSpec); + delete pEntry; + result = TRUE; + } + + return result; +} void AppDomain::SetupSharedStatics() { @@ -6484,6 +6534,44 @@ HMODULE AppDomain::FindUnmanagedImageInCache(LPCWSTR libraryName) RETURN (HMODULE) m_UnmanagedCache.LookupEntry(&spec, 0); } +BOOL AppDomain::RemoveFileFromCache(PEAssembly *pFile) +{ + CONTRACTL + { + GC_TRIGGERS; + PRECONDITION(CheckPointer(pFile)); + } + CONTRACTL_END; + + LoadLockHolder lock(this); + FileLoadLock *fileLock = (FileLoadLock *)lock->FindFileLock(pFile); + + if (fileLock == NULL) + return FALSE; + + VERIFY(lock->Unlink(fileLock)); + + fileLock->Release(); + + return TRUE; +} + +BOOL AppDomain::RemoveAssemblyFromCache(DomainAssembly* pAssembly) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(pAssembly)); + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + CrstHolder holder(&m_DomainCacheCrst); + + return m_AssemblyCache.RemoveAssembly(pAssembly); +} BOOL AppDomain::IsCached(AssemblySpec *pSpec) { diff --git a/src/vm/appdomain.hpp b/src/vm/appdomain.hpp index 4990f75f33..eb28f3ae11 100644 --- a/src/vm/appdomain.hpp +++ b/src/vm/appdomain.hpp @@ -2295,7 +2295,7 @@ private: GUID m_guidMVID; }; - class NativeImageDependenciesTraits : public NoRemoveSHashTraits<DefaultSHashTraits<NativeImageDependenciesEntry *> > + class NativeImageDependenciesTraits : public DeleteElementsOnDestructSHashTraits<DefaultSHashTraits<NativeImageDependenciesEntry *> > { public: typedef BaseAssemblySpec *key_t; @@ -2316,6 +2316,7 @@ private: public: void CheckForMismatchedNativeImages(AssemblySpec * pSpec, const GUID * pGuid); + BOOL RemoveNativeImageDependency(AssemblySpec* pSpec); public: class PathIterator @@ -2425,15 +2426,19 @@ public: void CacheStringsForDAC(); BOOL AddFileToCache(AssemblySpec* pSpec, PEAssembly *pFile, BOOL fAllowFailure = FALSE); + BOOL RemoveFileFromCache(PEAssembly *pFile); + BOOL AddAssemblyToCache(AssemblySpec* pSpec, DomainAssembly *pAssembly); + BOOL RemoveAssemblyFromCache(DomainAssembly* pAssembly); + BOOL AddExceptionToCache(AssemblySpec* pSpec, Exception *ex); void AddUnmanagedImageToCache(LPCWSTR libraryName, HMODULE hMod); HMODULE FindUnmanagedImageInCache(LPCWSTR libraryName); //**************************************************************************************** // - // Adds an assembly to the domain. + // Adds or removes an assembly to the domain. void AddAssembly(DomainAssembly * assem); - void RemoveAssembly_Unlocked(DomainAssembly * pAsm); + void RemoveAssembly(DomainAssembly * pAsm); BOOL ContainsAssembly(Assembly * assem); diff --git a/src/vm/arm/cgencpu.h b/src/vm/arm/cgencpu.h index 7f02b7b090..745626bd40 100644 --- a/src/vm/arm/cgencpu.h +++ b/src/vm/arm/cgencpu.h @@ -964,6 +964,7 @@ public: #endif // FEATURE_SHARE_GENERIC_CODE static Stub * CreateTailCallCopyArgsThunk(CORINFO_SIG_INFO * pSig, + MethodDesc* pMD, CorInfoHelperTailCallSpecialHandling flags); private: diff --git a/src/vm/arm/stubs.cpp b/src/vm/arm/stubs.cpp index cb9ff602ff..d97062d40f 100644 --- a/src/vm/arm/stubs.cpp +++ b/src/vm/arm/stubs.cpp @@ -3161,6 +3161,7 @@ void StubLinkerCPU::ThumbCopyOneTailCallArg(UINT * pnSrcAlign, const ArgLocDesc Stub * StubLinkerCPU::CreateTailCallCopyArgsThunk(CORINFO_SIG_INFO * pSig, + MethodDesc* pMD, CorInfoHelperTailCallSpecialHandling flags) { STANDARD_VM_CONTRACT; @@ -3407,8 +3408,8 @@ Stub * StubLinkerCPU::CreateTailCallCopyArgsThunk(CORINFO_SIG_INFO * pSig, pSl->ThumbEmitJumpRegister(thumbRegLr); } - - return pSl->Link(); + LoaderHeap* pHeap = pMD->GetLoaderAllocatorForCode()->GetStubHeap(); + return pSl->Link(pHeap); } diff --git a/src/vm/assembly.cpp b/src/vm/assembly.cpp index 4f9f13cf01..0eb1e2a8c6 100644 --- a/src/vm/assembly.cpp +++ b/src/vm/assembly.cpp @@ -402,53 +402,8 @@ void Assembly::Terminate( BOOL signalProfiler ) m_pClassLoader = NULL; } - if (m_pLoaderAllocator != NULL) - { - if (IsCollectible()) - { - // This cleanup code starts resembling parts of AppDomain::Terminate too much. - // It would be useful to reduce duplication and also establish clear responsibilites - // for LoaderAllocator::Destroy, Assembly::Terminate, LoaderAllocator::Terminate - // and LoaderAllocator::~LoaderAllocator. We need to establish how these - // cleanup paths interact with app-domain unload and process tear-down, too. - - if (!IsAtProcessExit()) - { - // Suspend the EE to do some clean up that can only occur - // while no threads are running. - GCX_COOP (); // SuspendEE may require current thread to be in Coop mode - // SuspendEE cares about the reason flag only when invoked for a GC - // Other values are typically ignored. If using SUSPEND_FOR_APPDOMAIN_SHUTDOWN - // is inappropriate, we can introduce a new flag or hijack an unused one. - ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_APPDOMAIN_SHUTDOWN); - } - - ExecutionManager::Unload(m_pLoaderAllocator); - - m_pLoaderAllocator->UninitVirtualCallStubManager(); - MethodTable::ClearMethodDataCache(); - _ASSERTE(m_pDomain->IsAppDomain()); - AppDomain *pAppDomain = m_pDomain->AsAppDomain(); - ClearJitGenericHandleCache(pAppDomain); - - if (!IsAtProcessExit()) - { - // Resume the EE. - ThreadSuspend::RestartEE(FALSE, TRUE); - } - - // Once the manifest file is tenured, the managed LoaderAllocatorScout is responsible for cleanup. - if (m_pManifest != NULL && m_pManifest->IsTenured()) - { - pAppDomain->RegisterLoaderAllocatorForDeletion(m_pLoaderAllocator); - } - } - m_pLoaderAllocator = NULL; - } - COUNTER_ONLY(GetPerfCounters().m_Loading.cAssemblies--); - #ifdef PROFILING_SUPPORTED if (CORProfilerTrackAssemblyLoads()) { @@ -709,6 +664,7 @@ Assembly *Assembly::CreateDynamic(AppDomain *pDomain, CreateDynamicAssemblyArgs if ((args->access & ASSEMBLY_ACCESS_COLLECT) != 0) { AssemblyLoaderAllocator *pAssemblyLoaderAllocator = new AssemblyLoaderAllocator(); + pAssemblyLoaderAllocator->SetCollectible(); pLoaderAllocator = pAssemblyLoaderAllocator; // Some of the initialization functions are not virtual. Call through the derived class @@ -728,6 +684,12 @@ Assembly *Assembly::CreateDynamic(AppDomain *pDomain, CreateDynamicAssemblyArgs // Create a domain assembly pDomainAssembly = new DomainAssembly(pDomain, pFile, pLoaderAllocator); + if (pDomainAssembly->IsCollectible()) + { + // We add the assembly to the LoaderAllocator only when we are sure that it can be added + // and won't be deleted in case of a concurrent load from the same ALC + ((AssemblyLoaderAllocator *)(LoaderAllocator *)pLoaderAllocator)->AddDomainAssembly(pDomainAssembly); + } } // Start loading process @@ -749,7 +711,7 @@ Assembly *Assembly::CreateDynamic(AppDomain *pDomain, CreateDynamicAssemblyArgs { // Initializing the virtual call stub manager is delayed to remove the need for the LoaderAllocator destructor to properly handle // uninitializing the VSD system. (There is a need to suspend the runtime, and that's tricky) - pLoaderAllocator->InitVirtualCallStubManager(pDomain, TRUE); + pLoaderAllocator->InitVirtualCallStubManager(pDomain); } } diff --git a/src/vm/assemblynative.cpp b/src/vm/assemblynative.cpp index be1cbc39b9..e658f25822 100644 --- a/src/vm/assemblynative.cpp +++ b/src/vm/assemblynative.cpp @@ -1184,7 +1184,8 @@ void QCALLTYPE AssemblyNative::GetImageRuntimeVersion(QCall::AssemblyHandle pAss } /*static*/ -INT_PTR QCALLTYPE AssemblyNative::InitializeAssemblyLoadContext(INT_PTR ptrManagedAssemblyLoadContext, BOOL fRepresentsTPALoadContext) + +INT_PTR QCALLTYPE AssemblyNative::InitializeAssemblyLoadContext(INT_PTR ptrManagedAssemblyLoadContext, BOOL fRepresentsTPALoadContext, BOOL fIsCollectible) { QCALL_CONTRACT; @@ -1203,7 +1204,41 @@ INT_PTR QCALLTYPE AssemblyNative::InitializeAssemblyLoadContext(INT_PTR ptrManag { // Initialize a custom Assembly Load Context CLRPrivBinderAssemblyLoadContext *pBindContext = NULL; - IfFailThrow(CLRPrivBinderAssemblyLoadContext::SetupContext(pCurDomain->GetId().m_dwId, pTPABinderContext, ptrManagedAssemblyLoadContext, &pBindContext)); + + AssemblyLoaderAllocator* loaderAllocator = NULL; + OBJECTHANDLE loaderAllocatorHandle = NULL; + + if (fIsCollectible) + { + // Create a new AssemblyLoaderAllocator for an AssemblyLoadContext + loaderAllocator = new AssemblyLoaderAllocator(); + loaderAllocator->SetCollectible(); + + GCX_COOP(); + LOADERALLOCATORREF pManagedLoaderAllocator = NULL; + GCPROTECT_BEGIN(pManagedLoaderAllocator); + { + GCX_PREEMP(); + // Some of the initialization functions are not virtual. Call through the derived class + // to prevent calling the base class version. + loaderAllocator->Init(pCurDomain); + loaderAllocator->InitVirtualCallStubManager(pCurDomain); + + // Setup the managed proxy now, but do not actually transfer ownership to it. + // Once everything is setup and nothing can fail anymore, the ownership will be + // atomically transfered by call to LoaderAllocator::ActivateManagedTracking(). + loaderAllocator->SetupManagedTracking(&pManagedLoaderAllocator); + } + + // Create a strong handle to the LoaderAllocator + loaderAllocatorHandle = pCurDomain->CreateHandle(pManagedLoaderAllocator); + + GCPROTECT_END(); + + loaderAllocator->ActivateManagedTracking(); + } + + IfFailThrow(CLRPrivBinderAssemblyLoadContext::SetupContext(pCurDomain->GetId().m_dwId, pTPABinderContext, loaderAllocator, loaderAllocatorHandle, ptrManagedAssemblyLoadContext, &pBindContext)); ptrNativeAssemblyLoadContext = reinterpret_cast<INT_PTR>(pBindContext); } else @@ -1227,6 +1262,24 @@ INT_PTR QCALLTYPE AssemblyNative::InitializeAssemblyLoadContext(INT_PTR ptrManag } /*static*/ +void QCALLTYPE AssemblyNative::PrepareForAssemblyLoadContextRelease(INT_PTR ptrNativeAssemblyLoadContext, INT_PTR ptrManagedStrongAssemblyLoadContext) +{ + QCALL_CONTRACT; + + BOOL fDestroyed = FALSE; + + BEGIN_QCALL; + + + { + GCX_COOP(); + reinterpret_cast<CLRPrivBinderAssemblyLoadContext *>(ptrNativeAssemblyLoadContext)->PrepareForLoadContextRelease(ptrManagedStrongAssemblyLoadContext); + } + + END_QCALL; +} + +/*static*/ BOOL QCALLTYPE AssemblyNative::OverrideDefaultAssemblyLoadContextForCurrentDomain(INT_PTR ptrNativeAssemblyLoadContext) { QCALL_CONTRACT; diff --git a/src/vm/assemblynative.hpp b/src/vm/assemblynative.hpp index 655f5c7ff4..0bdb2c31ed 100644 --- a/src/vm/assemblynative.hpp +++ b/src/vm/assemblynative.hpp @@ -117,7 +117,8 @@ public: // PEFile QCalls // - static INT_PTR QCALLTYPE InitializeAssemblyLoadContext(INT_PTR ptrManagedAssemblyLoadContext, BOOL fRepresentsTPALoadContext); + static INT_PTR QCALLTYPE InitializeAssemblyLoadContext(INT_PTR ptrManagedAssemblyLoadContext, BOOL fRepresentsTPALoadContext, BOOL fIsCollectible); + static void QCALLTYPE PrepareForAssemblyLoadContextRelease(INT_PTR ptrNativeAssemblyLoadContext, INT_PTR ptrManagedStrongAssemblyLoadContext); static BOOL QCALLTYPE OverrideDefaultAssemblyLoadContextForCurrentDomain(INT_PTR ptrNativeAssemblyLoadContext); static BOOL QCALLTYPE CanUseAppPathAssemblyLoadContextInCurrentDomain(); static void QCALLTYPE LoadFromPath(INT_PTR ptrNativeAssemblyLoadContext, LPCWSTR pwzILPath, LPCWSTR pwzNIPath, QCall::ObjectHandleOnStack retLoadedAssembly); diff --git a/src/vm/assemblyspec.cpp b/src/vm/assemblyspec.cpp index f274c13c61..2d3d76cf03 100644 --- a/src/vm/assemblyspec.cpp +++ b/src/vm/assemblyspec.cpp @@ -1267,7 +1267,7 @@ void AssemblySpecBindingCache::Init(CrstBase *pCrst, LoaderHeap *pHeap) m_pHeap = pHeap; } -AssemblySpecBindingCache::AssemblyBinding* AssemblySpecBindingCache::GetAssemblyBindingEntryForAssemblySpec(AssemblySpec* pSpec, BOOL fThrow) +AssemblySpecBindingCache::AssemblyBinding* AssemblySpecBindingCache::LookupInternal(AssemblySpec* pSpec, BOOL fThrow) { CONTRACTL { @@ -1288,9 +1288,9 @@ AssemblySpecBindingCache::AssemblyBinding* AssemblySpecBindingCache::GetAssembly } CONTRACTL_END; - AssemblyBinding* pEntry = (AssemblyBinding *) INVALIDENTRY; UPTR key = (UPTR)pSpec->Hash(); - + UPTR lookupKey = key; + // On CoreCLR, we will use the BinderID as the key ICLRPrivBinder *pBinderContextForLookup = NULL; AppDomain *pSpecDomain = pSpec->GetAppDomain(); @@ -1309,7 +1309,7 @@ AssemblySpecBindingCache::AssemblyBinding* AssemblySpecBindingCache::GetAssembly if (pBinderContextForLookup != NULL) { // We are working with the actual binding context in which the assembly was expected to be loaded. - // Thus, we dont need to get it from the parent assembly. + // Thus, we don't need to get it from the parent assembly. fGetBindingContextFromParent = false; } @@ -1324,7 +1324,6 @@ AssemblySpecBindingCache::AssemblyBinding* AssemblySpecBindingCache::GetAssembly } } - UPTR lookupKey = key; if (pBinderContextForLookup) { UINT_PTR binderID = 0; @@ -1332,9 +1331,9 @@ AssemblySpecBindingCache::AssemblyBinding* AssemblySpecBindingCache::GetAssembly _ASSERTE(SUCCEEDED(hr)); lookupKey = key^binderID; } - - pEntry = (AssemblyBinding *) m_map.LookupValue(lookupKey, pSpec); - + + AssemblyBinding* pEntry = (AssemblyBinding *)m_map.LookupValue(lookupKey, pSpec); + // Reset the binding context if one was originally never present in the AssemblySpec and we didnt find any entry // in the cache. if (fGetBindingContextFromParent) @@ -1351,8 +1350,7 @@ AssemblySpecBindingCache::AssemblyBinding* AssemblySpecBindingCache::GetAssembly BOOL AssemblySpecBindingCache::Contains(AssemblySpec *pSpec) { WRAPPER_NO_CONTRACT; - - return (GetAssemblyBindingEntryForAssemblySpec(pSpec, TRUE) != (AssemblyBinding *) INVALIDENTRY); + return (LookupInternal(pSpec, TRUE) != (AssemblyBinding *) INVALIDENTRY); } DomainAssembly *AssemblySpecBindingCache::LookupAssembly(AssemblySpec *pSpec, @@ -1378,7 +1376,7 @@ DomainAssembly *AssemblySpecBindingCache::LookupAssembly(AssemblySpec *pSpec, AssemblyBinding *entry = (AssemblyBinding *) INVALIDENTRY; - entry = GetAssemblyBindingEntryForAssemblySpec(pSpec, fThrow); + entry = LookupInternal(pSpec, fThrow); if (entry == (AssemblyBinding *) INVALIDENTRY) RETURN NULL; @@ -1414,9 +1412,8 @@ PEAssembly *AssemblySpecBindingCache::LookupFile(AssemblySpec *pSpec, BOOL fThro } CONTRACT_END; - AssemblyBinding *entry = (AssemblyBinding *) INVALIDENTRY; - - entry = GetAssemblyBindingEntryForAssemblySpec(pSpec, fThrow); + AssemblyBinding *entry = (AssemblyBinding *) INVALIDENTRY; + entry = LookupInternal(pSpec, fThrow); if (entry == (AssemblyBinding *) INVALIDENTRY) RETURN NULL; @@ -1545,6 +1542,7 @@ BOOL AssemblySpecBindingCache::StoreAssembly(AssemblySpec *pSpec, DomainAssembly // On CoreCLR, we will use the BinderID as the key ICLRPrivBinder* pBinderContextForLookup = pAssembly->GetFile()->GetBindingContext(); + _ASSERTE(pBinderContextForLookup || pAssembly->GetFile()->IsSystem()); if (pBinderContextForLookup) { @@ -1558,15 +1556,21 @@ BOOL AssemblySpecBindingCache::StoreAssembly(AssemblySpec *pSpec, DomainAssembly pSpec->SetBindingContext(pBinderContextForLookup); } } - + AssemblyBinding *entry = (AssemblyBinding *) m_map.LookupValue(key, pSpec); if (entry == (AssemblyBinding *) INVALIDENTRY) { AssemblyBindingHolder abHolder; - entry = abHolder.CreateAssemblyBinding(m_pHeap); - entry->Init(pSpec,pAssembly->GetFile(),pAssembly,NULL,m_pHeap, abHolder.GetPamTracker()); + LoaderHeap* pHeap = m_pHeap; + if (pAssembly->IsCollectible()) + { + pHeap = pAssembly->GetLoaderAllocator()->GetHighFrequencyHeap(); + } + + entry = abHolder.CreateAssemblyBinding(pHeap); + entry->Init(pSpec,pAssembly->GetFile(),pAssembly,NULL,pHeap, abHolder.GetPamTracker()); m_map.InsertValue(key, entry); @@ -1625,6 +1629,7 @@ BOOL AssemblySpecBindingCache::StoreFile(AssemblySpec *pSpec, PEAssembly *pFile) // On CoreCLR, we will use the BinderID as the key ICLRPrivBinder* pBinderContextForLookup = pFile->GetBindingContext(); + _ASSERTE(pBinderContextForLookup || pFile->IsSystem()); if (pBinderContextForLookup) { @@ -1644,9 +1649,27 @@ BOOL AssemblySpecBindingCache::StoreFile(AssemblySpec *pSpec, PEAssembly *pFile) if (entry == (AssemblyBinding *) INVALIDENTRY) { AssemblyBindingHolder abHolder; - entry = abHolder.CreateAssemblyBinding(m_pHeap); - entry->Init(pSpec,pFile,NULL,NULL,m_pHeap, abHolder.GetPamTracker()); + LoaderHeap* pHeap = m_pHeap; + +#ifndef CROSSGEN_COMPILE + if (pBinderContextForLookup != NULL) + { + LoaderAllocator* pLoaderAllocator = NULL; + + // Assemblies loaded with AssemblyLoadContext need to use a different heap if + // marked as collectible + if (SUCCEEDED(pBinderContextForLookup->GetLoaderAllocator((LPVOID*)&pLoaderAllocator))) + { + _ASSERTE(pLoaderAllocator != NULL); + pHeap = pLoaderAllocator->GetHighFrequencyHeap(); + } + } +#endif // !CROSSGEN_COMPILE + + entry = abHolder.CreateAssemblyBinding(pHeap); + + entry->Init(pSpec,pFile,NULL,NULL,pHeap, abHolder.GetPamTracker()); m_map.InsertValue(key, entry); abHolder.SuppressRelease(); @@ -1694,7 +1717,7 @@ BOOL AssemblySpecBindingCache::StoreException(AssemblySpec *pSpec, Exception* pE UPTR key = (UPTR)pSpec->Hash(); - AssemblyBinding *entry = GetAssemblyBindingEntryForAssemblySpec(pSpec, TRUE); + AssemblyBinding *entry = LookupInternal(pSpec, TRUE); if (entry == (AssemblyBinding *) INVALIDENTRY) { // TODO: Merge this with the failure lookup in the binder @@ -1751,6 +1774,40 @@ BOOL AssemblySpecBindingCache::StoreException(AssemblySpec *pSpec, Exception* pE } } +BOOL AssemblySpecBindingCache::RemoveAssembly(DomainAssembly* pAssembly) +{ + CONTRACT(BOOL) + { + INSTANCE_CHECK; + NOTHROW; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(pAssembly != NULL); + } + CONTRACT_END; + BOOL result = FALSE; + PtrHashMap::PtrIterator i = m_map.begin(); + while (!i.end()) + { + AssemblyBinding* entry = (AssemblyBinding*)i.GetValue(); + if (entry->GetAssembly() == pAssembly) + { + UPTR key = i.GetKey(); + m_map.DeleteValue(key, entry); + + if (m_pHeap == NULL) + delete entry; + else + entry->~AssemblyBinding(); + + result = TRUE; + } + ++i; + } + + RETURN result; +} + /* static */ BOOL AssemblySpecHash::CompareSpecs(UPTR u1, UPTR u2) { @@ -1759,9 +1816,6 @@ BOOL AssemblySpecHash::CompareSpecs(UPTR u1, UPTR u2) return AssemblySpecBindingCache::CompareSpecs(u1,u2); } - - - /* static */ BOOL AssemblySpecBindingCache::CompareSpecs(UPTR u1, UPTR u2) { @@ -1772,8 +1826,6 @@ BOOL AssemblySpecBindingCache::CompareSpecs(UPTR u1, UPTR u2) return a1->CompareEx(a2); } - - /* static */ BOOL DomainAssemblyCache::CompareBindingSpec(UPTR spec1, UPTR spec2) { @@ -1785,7 +1837,6 @@ BOOL DomainAssemblyCache::CompareBindingSpec(UPTR spec1, UPTR spec2) return pSpec1->CompareEx(&pEntry2->spec); } - DomainAssemblyCache::AssemblyEntry* DomainAssemblyCache::LookupEntry(AssemblySpec* pSpec) { CONTRACT (DomainAssemblyCache::AssemblyEntry*) diff --git a/src/vm/assemblyspec.hpp b/src/vm/assemblyspec.hpp index 6db0d1ac8f..bbcc2ea2b4 100644 --- a/src/vm/assemblyspec.hpp +++ b/src/vm/assemblyspec.hpp @@ -573,7 +573,7 @@ class AssemblySpecBindingCache PtrHashMap m_map; LoaderHeap *m_pHeap; - AssemblySpecBindingCache::AssemblyBinding* GetAssemblyBindingEntryForAssemblySpec(AssemblySpec* pSpec, BOOL fThrow); + AssemblySpecBindingCache::AssemblyBinding* LookupInternal(AssemblySpec* pSpec, BOOL fThrow = FALSE); public: @@ -595,12 +595,14 @@ class AssemblySpecBindingCache BOOL StoreException(AssemblySpec *pSpec, Exception* pEx); + BOOL RemoveAssembly(DomainAssembly* pAssembly); + DWORD Hash(AssemblySpec *pSpec) { WRAPPER_NO_CONTRACT; return pSpec->Hash(); } - + #if !defined(DACCESS_COMPILE) void GetAllAssemblies(SetSHash<PTR_DomainAssembly>& assemblyList) { diff --git a/src/vm/ceeload.cpp b/src/vm/ceeload.cpp index 8aaaae4713..cd9e485af7 100644 --- a/src/vm/ceeload.cpp +++ b/src/vm/ceeload.cpp @@ -3195,7 +3195,10 @@ void Module::SetDomainFile(DomainFile *pDomainFile) // Allocate static handles now. // NOTE: Bootstrapping issue with mscorlib - we will manually allocate later - if (g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT] != NULL) + // If the assembly is collectible, we don't initialize static handles for them + // as it is currently initialized through the DomainLocalModule::PopulateClass in MethodTable::CheckRunClassInitThrowing + // (If we don't do this, it would allocate here unused regular static handles that will be overridden later) + if (g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT] != NULL && !GetAssembly()->IsCollectible()) AllocateRegularStaticHandles(pDomainFile->GetAppDomain()); } diff --git a/src/vm/clrprivbinderloadfile.h b/src/vm/clrprivbinderloadfile.h index 02c4053ff4..0aa2f33ef6 100644 --- a/src/vm/clrprivbinderloadfile.h +++ b/src/vm/clrprivbinderloadfile.h @@ -57,6 +57,10 @@ public: ICLRPrivAssembly ** ppAssembly) { STATIC_CONTRACT_WRAPPER; return E_FAIL; } + STDMETHOD(GetLoaderAllocator)( + /* [retval][out] */ LoaderAllocator** pLoaderAllocator) + { STATIC_CONTRACT_WRAPPER; return E_FAIL; } + //============================================================================================= // Class methods //--------------------------------------------------------------------------------------------- @@ -145,4 +149,12 @@ public: HRESULT * pResult, ICLRPrivAssembly ** ppAssembly) { STATIC_CONTRACT_WRAPPER; return m_pBinder->FindAssemblyBySpec(pvAppDomain, pvAssemblySpec, pResult, ppAssembly); } + + //--------------------------------------------------------------------------------------------- + STDMETHOD(GetLoaderAllocator)( + LoaderAllocator** pLoaderAllocator) + { + WRAPPER_NO_CONTRACT; + return m_pBinder->GetLoaderAllocator(pLoaderAllocator); + } }; diff --git a/src/vm/clrprivbinderwinrt.h b/src/vm/clrprivbinderwinrt.h index 14c467044b..2bf1061d18 100644 --- a/src/vm/clrprivbinderwinrt.h +++ b/src/vm/clrprivbinderwinrt.h @@ -176,6 +176,12 @@ public: } } + STDMETHOD(GetLoaderAllocator)( + LPVOID * pLoaderAllocator) + { + return E_FAIL; + } + HRESULT FindWinRTAssemblyBySpec( LPVOID pvAppDomain, LPVOID pvAssemblySpec, @@ -382,6 +388,13 @@ public: return m_pBinder->FindAssemblyBySpec(pvAppDomain, pvAssemblySpec, pResult, ppAssembly); } + STDMETHOD(GetLoaderAllocator)( + LPVOID * pLoaderAllocator) + { + WRAPPER_NO_CONTRACT; + return m_pBinder->GetLoaderAllocator(pLoaderAllocator); + } + //============================================================================================= // ICLRPrivAssembly interface methods diff --git a/src/vm/comdelegate.cpp b/src/vm/comdelegate.cpp index d80d0deb89..749310bb67 100644 --- a/src/vm/comdelegate.cpp +++ b/src/vm/comdelegate.cpp @@ -508,51 +508,6 @@ VOID GenerateShuffleArray(MethodDesc* pInvoke, MethodDesc *pTargetMeth, SArray<S } -class ShuffleThunkCache : public StubCacheBase -{ -private: - //--------------------------------------------------------- - // Compile a static delegate shufflethunk. Always returns - // STANDALONE since we don't interpret these things. - //--------------------------------------------------------- - virtual void CompileStub(const BYTE *pRawStub, - StubLinker *pstublinker) - { - STANDARD_VM_CONTRACT; - - ((CPUSTUBLINKER*)pstublinker)->EmitShuffleThunk((ShuffleEntry*)pRawStub); - } - - //--------------------------------------------------------- - // Tells the StubCacheBase the length of a ShuffleEntryArray. - //--------------------------------------------------------- - virtual UINT Length(const BYTE *pRawStub) - { - LIMITED_METHOD_CONTRACT; - ShuffleEntry *pse = (ShuffleEntry*)pRawStub; - while (pse->srcofs != ShuffleEntry::SENTINEL) - { - pse++; - } - return sizeof(ShuffleEntry) * (UINT)(1 + (pse - (ShuffleEntry*)pRawStub)); - } - - virtual void AddStub(const BYTE* pRawStub, Stub* pNewStub) - { - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - -#ifndef CROSSGEN_COMPILE - DelegateInvokeStubManager::g_pManager->AddStub(pNewStub); -#endif - } -}; - ShuffleThunkCache *COMDelegate::m_pShuffleThunkCache = NULL; MulticastStubCache *COMDelegate::m_pSecureDelegateStubCache = NULL; MulticastStubCache *COMDelegate::m_pMulticastStubCache = NULL; @@ -579,7 +534,7 @@ void COMDelegate::Init() LockOwner lock = {&COMDelegate::s_DelegateToFPtrHashCrst, IsOwnerOfCrst}; s_pDelegateToFPtrHash->Init(TRUE, &lock); - m_pShuffleThunkCache = new ShuffleThunkCache(); + m_pShuffleThunkCache = new ShuffleThunkCache(SystemDomain::GetGlobalLoaderAllocator()->GetStubHeap()); m_pMulticastStubCache = new MulticastStubCache(); m_pSecureDelegateStubCache = new MulticastStubCache(); } @@ -642,7 +597,15 @@ Stub* COMDelegate::SetupShuffleThunk(MethodTable * pDelMT, MethodDesc *pTargetMe StackSArray<ShuffleEntry> rShuffleEntryArray; GenerateShuffleArray(pMD, pTargetMeth, &rShuffleEntryArray); - Stub* pShuffleThunk = m_pShuffleThunkCache->Canonicalize((const BYTE *)&rShuffleEntryArray[0]); + ShuffleThunkCache* pShuffleThunkCache = m_pShuffleThunkCache; + + LoaderAllocator* pLoaderAllocator = pDelMT->GetLoaderAllocator(); + if (pLoaderAllocator->IsCollectible()) + { + pShuffleThunkCache = ((AssemblyLoaderAllocator*)pLoaderAllocator)->GetShuffleThunkCache(); + } + + Stub* pShuffleThunk = pShuffleThunkCache->Canonicalize((const BYTE *)&rShuffleEntryArray[0]); if (!pShuffleThunk) { COMPlusThrowOM(); diff --git a/src/vm/comdelegate.h b/src/vm/comdelegate.h index 1f6d10b907..5877b48185 100644 --- a/src/vm/comdelegate.h +++ b/src/vm/comdelegate.h @@ -224,4 +224,53 @@ struct ShuffleEntry #include <poppack.h> +class ShuffleThunkCache : public StubCacheBase +{ +public: + ShuffleThunkCache(LoaderHeap* heap) : StubCacheBase(heap) + { + } +private: + //--------------------------------------------------------- + // Compile a static delegate shufflethunk. Always returns + // STANDALONE since we don't interpret these things. + //--------------------------------------------------------- + virtual void CompileStub(const BYTE *pRawStub, + StubLinker *pstublinker) + { + STANDARD_VM_CONTRACT; + + ((CPUSTUBLINKER*)pstublinker)->EmitShuffleThunk((ShuffleEntry*)pRawStub); + } + + //--------------------------------------------------------- + // Tells the StubCacheBase the length of a ShuffleEntryArray. + //--------------------------------------------------------- + virtual UINT Length(const BYTE *pRawStub) + { + LIMITED_METHOD_CONTRACT; + ShuffleEntry *pse = (ShuffleEntry*)pRawStub; + while (pse->srcofs != ShuffleEntry::SENTINEL) + { + pse++; + } + return sizeof(ShuffleEntry) * (UINT)(1 + (pse - (ShuffleEntry*)pRawStub)); + } + + virtual void AddStub(const BYTE* pRawStub, Stub* pNewStub) + { + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + +#ifndef CROSSGEN_COMPILE + DelegateInvokeStubManager::g_pManager->AddStub(pNewStub); +#endif + } +}; + #endif // _COMDELEGATE_H_ diff --git a/src/vm/coreassemblyspec.cpp b/src/vm/coreassemblyspec.cpp index 1d3567e769..5b606dcd38 100644 --- a/src/vm/coreassemblyspec.cpp +++ b/src/vm/coreassemblyspec.cpp @@ -379,7 +379,7 @@ HRESULT BaseAssemblySpec::ParseName() #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) if (pDomain->GetFusionContext() != pDomain->GetTPABinderContext()) { - pAppContext = (static_cast<CLRPrivBinderAssemblyLoadContext *>(pIUnknownBinder))->GetAppContext(); + pAppContext = (static_cast<CLRPrivBinderAssemblyLoadContext *>(static_cast<ICLRPrivBinder*>(pIUnknownBinder)))->GetAppContext(); } else #endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) diff --git a/src/vm/domainfile.cpp b/src/vm/domainfile.cpp index 314f165eea..6b30ee7c3a 100644 --- a/src/vm/domainfile.cpp +++ b/src/vm/domainfile.cpp @@ -1473,7 +1473,9 @@ DomainAssembly::DomainAssembly(AppDomain *pDomain, PEFile *pFile, LoaderAllocato m_fCollectible(pLoaderAllocator->IsCollectible()), m_fHostAssemblyPublished(false), m_fCalculatedShouldLoadDomainNeutral(false), - m_fShouldLoadDomainNeutral(false) + m_fShouldLoadDomainNeutral(false), + m_pLoaderAllocator(pLoaderAllocator), + m_NextDomainAssemblyInSameALC(NULL) { CONTRACTL { @@ -1485,13 +1487,6 @@ DomainAssembly::DomainAssembly(AppDomain *pDomain, PEFile *pFile, LoaderAllocato pFile->ValidateForExecution(); -#ifndef CROSSGEN_COMPILE - if (m_fCollectible) - { - ((AssemblyLoaderAllocator *)pLoaderAllocator)->SetDomainAssembly(this); - } -#endif - // !!! backout m_hExposedAssemblyObject = NULL; @@ -2005,7 +2000,7 @@ void DomainAssembly::Allocate() // Go ahead and create new shared version of the assembly if possible // <TODO> We will need to pass a valid OBJECREF* here in the future when we implement SCU </TODO> - assemblyHolder = pAssembly = Assembly::Create(pSharedDomain, GetFile(), GetDebuggerInfoBits(), FALSE, pamTracker, NULL); + assemblyHolder = pAssembly = Assembly::Create(pSharedDomain, GetFile(), GetDebuggerInfoBits(), this->IsCollectible(), pamTracker, this->IsCollectible() ? this->GetLoaderAllocator() : NULL); if (MissingDependenciesCheckDone()) pAssembly->SetMissingDependenciesCheckDone(); @@ -2040,7 +2035,7 @@ void DomainAssembly::Allocate() // <TODO> We will need to pass a valid OBJECTREF* here in the future when we implement SCU </TODO> SharedDomain * pSharedDomain = SharedDomain::GetDomain(); - assemblyHolder = pAssembly = Assembly::Create(pSharedDomain, GetFile(), GetDebuggerInfoBits(), FALSE, pamTracker, NULL); + assemblyHolder = pAssembly = Assembly::Create(pSharedDomain, GetFile(), GetDebuggerInfoBits(), this->IsCollectible(), pamTracker, this->IsCollectible() ? this->GetLoaderAllocator() : NULL); pAssembly->SetIsTenured(); } #endif // FEATURE_LOADER_OPTIMIZATION @@ -2051,7 +2046,7 @@ void DomainAssembly::Allocate() GetFile()->MakeMDImportPersistent(); // <TODO> We will need to pass a valid OBJECTREF* here in the future when we implement SCU </TODO> - assemblyHolder = pAssembly = Assembly::Create(m_pDomain, GetFile(), GetDebuggerInfoBits(), FALSE, pamTracker, NULL); + assemblyHolder = pAssembly = Assembly::Create(m_pDomain, GetFile(), GetDebuggerInfoBits(), this->IsCollectible(), pamTracker, this->IsCollectible() ? this->GetLoaderAllocator() : NULL); assemblyHolder->SetIsTenured(); } diff --git a/src/vm/domainfile.h b/src/vm/domainfile.h index b163da87c6..070c616ecb 100644 --- a/src/vm/domainfile.h +++ b/src/vm/domainfile.h @@ -81,7 +81,7 @@ class DomainFile DomainFile() {LIMITED_METHOD_CONTRACT;}; #endif - LoaderAllocator *GetLoaderAllocator(); + virtual LoaderAllocator *GetLoaderAllocator(); PTR_AppDomain GetAppDomain() { @@ -513,6 +513,12 @@ public: return PTR_PEAssembly(m_pFile); } + LoaderAllocator *GetLoaderAllocator() + { + LIMITED_METHOD_CONTRACT; + return m_pLoaderAllocator; + } + #ifdef FEATURE_LOADER_OPTIMIZATION public: @@ -776,8 +782,21 @@ private: Volatile<bool> m_fHostAssemblyPublished; Volatile<bool> m_fCalculatedShouldLoadDomainNeutral; Volatile<bool> m_fShouldLoadDomainNeutral; + PTR_LoaderAllocator m_pLoaderAllocator; + DomainAssembly* m_NextDomainAssemblyInSameALC; public: + DomainAssembly* GetNextDomainAssemblyInSameALC() + { + return m_NextDomainAssemblyInSameALC; + } + + void SetNextDomainAssemblyInSameALC(DomainAssembly* domainAssembly) + { + _ASSERTE(m_NextDomainAssemblyInSameALC == NULL); + m_NextDomainAssemblyInSameALC = domainAssembly; + } + // Indicates if the assembly can be cached in a binding cache such as AssemblySpecBindingCache. inline bool CanUseWithBindingCache() { STATIC_CONTRACT_WRAPPER; return GetFile()->CanUseWithBindingCache(); } diff --git a/src/vm/dynamicmethod.cpp b/src/vm/dynamicmethod.cpp index 5fd552a0a1..ba97b3de5d 100644 --- a/src/vm/dynamicmethod.cpp +++ b/src/vm/dynamicmethod.cpp @@ -869,7 +869,7 @@ void DynamicMethodDesc::Destroy(BOOL fDomainUnload) if (pLoaderAllocator->Release()) { GCX_PREEMP(); - LoaderAllocator::GCLoaderAllocators(pLoaderAllocator->GetDomain()->AsAppDomain()); + LoaderAllocator::GCLoaderAllocators(pLoaderAllocator); } } } diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h index c147ae716e..7a6cb315cf 100644 --- a/src/vm/ecalllist.h +++ b/src/vm/ecalllist.h @@ -548,6 +548,7 @@ FCFuncEnd() FCFuncStart(gAssemblyLoadContextFuncs) QCFuncElement("InitializeAssemblyLoadContext", AssemblyNative::InitializeAssemblyLoadContext) + QCFuncElement("PrepareForAssemblyLoadContextRelease", AssemblyNative::PrepareForAssemblyLoadContextRelease) QCFuncElement("LoadFromPath", AssemblyNative::LoadFromPath) QCFuncElement("InternalLoadUnmanagedDllFromPath", AssemblyNative::InternalLoadUnmanagedDllFromPath) QCFuncElement("CanUseAppPathAssemblyLoadContextInCurrentDomain", AssemblyNative::CanUseAppPathAssemblyLoadContextInCurrentDomain) diff --git a/src/vm/eventtrace.cpp b/src/vm/eventtrace.cpp index 00d519d2b8..3e11983a56 100644 --- a/src/vm/eventtrace.cpp +++ b/src/vm/eventtrace.cpp @@ -7228,19 +7228,26 @@ VOID ETW::EnumerationLog::IterateCollectibleLoaderAllocator(AssemblyLoaderAlloca ETW::MethodLog::SendEventsForJitMethods(NULL, pLoaderAllocator, enumerationOptions); } - Assembly *pAssembly = pLoaderAllocator->Id()->GetDomainAssembly()->GetAssembly(); - _ASSERTE(!pAssembly->IsDomainNeutral()); // Collectible Assemblies are not domain neutral. - - DomainModuleIterator domainModuleIterator = pLoaderAllocator->Id()->GetDomainAssembly()->IterateModules(kModIterIncludeLoaded); - while (domainModuleIterator.Next()) + // Iterate on all DomainAssembly loaded from the same AssemblyLoaderAllocator + DomainAssemblyIterator domainAssemblyIt = pLoaderAllocator->Id()->GetDomainAssemblyIterator(); + while (!domainAssemblyIt.end()) { - Module *pModule = domainModuleIterator.GetModule(); - ETW::EnumerationLog::IterateModule(pModule, enumerationOptions); - } + Assembly *pAssembly = domainAssemblyIt->GetAssembly(); // TODO: handle iterator + _ASSERTE(!pAssembly->IsDomainNeutral()); // Collectible Assemblies are not domain neutral. - if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) - { - ETW::EnumerationLog::IterateAssembly(pAssembly, enumerationOptions); + DomainModuleIterator domainModuleIterator = domainAssemblyIt->IterateModules(kModIterIncludeLoaded); + while (domainModuleIterator.Next()) + { + Module *pModule = domainModuleIterator.GetModule(); + ETW::EnumerationLog::IterateModule(pModule, enumerationOptions); + } + + if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) + { + ETW::EnumerationLog::IterateAssembly(pAssembly, enumerationOptions); + } + + domainAssemblyIt++; } // Load Jit Method events diff --git a/src/vm/excep.cpp b/src/vm/excep.cpp index da07c5ad99..306e4d0d83 100644 --- a/src/vm/excep.cpp +++ b/src/vm/excep.cpp @@ -6357,7 +6357,8 @@ static STRINGREF MissingMemberException_FormatSignature_Internal(I1ARRAYREF* ppP psl->EmitUtf8(")"); } psl->Emit8('\0'); - pstub = psl->Link(); + + pstub = psl->Link(NULL); } pString = StringObject::NewString( (LPCUTF8)(pstub->GetEntryPoint()) ); diff --git a/src/vm/hash.h b/src/vm/hash.h index 0855ed481c..404392fff1 100644 --- a/src/vm/hash.h +++ b/src/vm/hash.h @@ -722,6 +722,14 @@ public: return iter.end(); } + UPTR GetKey() + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + return iter.GetKey(); + } + PTR_VOID GetValue() { WRAPPER_NO_CONTRACT; diff --git a/src/vm/i386/stublinkerx86.cpp b/src/vm/i386/stublinkerx86.cpp index 02f3a61c63..ac5006eb8f 100644 --- a/src/vm/i386/stublinkerx86.cpp +++ b/src/vm/i386/stublinkerx86.cpp @@ -6040,6 +6040,7 @@ static void AppendGCLayout(ULONGARRAY &gcLayout, size_t baseOffset, BOOL fIsType } Stub * StubLinkerCPU::CreateTailCallCopyArgsThunk(CORINFO_SIG_INFO * pSig, + MethodDesc* pMD, CorInfoHelperTailCallSpecialHandling flags) { STANDARD_VM_CONTRACT; @@ -6429,7 +6430,8 @@ Stub * StubLinkerCPU::CreateTailCallCopyArgsThunk(CORINFO_SIG_INFO * pSig, EncodeGCOffsets(pSl, gcLayout); } - return pSl->Link(); + LoaderHeap* pHeap = pMD->GetLoaderAllocatorForCode()->GetStubHeap(); + return pSl->Link(pHeap); } #endif // DACCESS_COMPILE diff --git a/src/vm/i386/stublinkerx86.h b/src/vm/i386/stublinkerx86.h index 55ba942920..44bfc79fd2 100644 --- a/src/vm/i386/stublinkerx86.h +++ b/src/vm/i386/stublinkerx86.h @@ -437,6 +437,7 @@ class StubLinkerCPU : public StubLinker #ifdef _TARGET_AMD64_ static Stub * CreateTailCallCopyArgsThunk(CORINFO_SIG_INFO * pSig, + MethodDesc* pMD, CorInfoHelperTailCallSpecialHandling flags); #endif // _TARGET_AMD64_ diff --git a/src/vm/jithelpers.cpp b/src/vm/jithelpers.cpp index 6d31a18729..9bb6d9889e 100644 --- a/src/vm/jithelpers.cpp +++ b/src/vm/jithelpers.cpp @@ -6238,6 +6238,8 @@ void InitJitHelperLogging() ThrowLastError(); } + LoaderHeap* pHeap = SystemDomain::GetGlobalLoaderAllocator()->GetStubHeap(); + // iterate through the jit helper tables replacing helpers with logging thunks // // NOTE: if NGEN'd images were NGEN'd with hard binding on then static helper @@ -6278,7 +6280,7 @@ void InitJitHelperLogging() #endif // _TARGET_AMD64_ pSl->EmitJITHelperLoggingThunk(GetEEFuncEntryPoint(hlpFunc->pfnHelper), (LPVOID)hlpFuncCount); - Stub* pStub = pSl->Link(); + Stub* pStub = pSl->Link(pHeap); hlpFunc->pfnHelper = (void*)pStub->GetEntryPoint(); } else @@ -6335,7 +6337,7 @@ void InitJitHelperLogging() #endif // _TARGET_AMD64_ pSl->EmitJITHelperLoggingThunk(GetEEFuncEntryPoint(dynamicHlpFunc->pfnHelper), (LPVOID)hlpFuncCount); - Stub* pStub = pSl->Link(); + Stub* pStub = pSl->Link(pHeap); dynamicHlpFunc->pfnHelper = (void*)pStub->GetEntryPoint(); } } diff --git a/src/vm/jitinterface.cpp b/src/vm/jitinterface.cpp index 0697adc0a7..4391167067 100644 --- a/src/vm/jitinterface.cpp +++ b/src/vm/jitinterface.cpp @@ -11731,14 +11731,13 @@ void* CEEJitInfo::getFieldAddress(CORINFO_FIELD_HANDLE fieldHnd, _ASSERTE(!pMT->ContainsGenericVariables()); - // We must not call here for statics of collectible types. - _ASSERTE(!pMT->Collectible()); - void *base = NULL; if (!field->IsRVA()) { // <REVISIT_TODO>@todo: assert that the current method being compiled is unshared</REVISIT_TODO> + // We must not call here for statics of collectible types. + _ASSERTE(!pMT->Collectible()); // Allocate space for the local class if necessary, but don't trigger // class construction. @@ -13760,7 +13759,7 @@ void* CEEInfo::getTailCallCopyArgsThunk(CORINFO_SIG_INFO *pSig, JIT_TO_EE_TRANSITION(); - Stub* pStub = CPUSTUBLINKER::CreateTailCallCopyArgsThunk(pSig, flags); + Stub* pStub = CPUSTUBLINKER::CreateTailCallCopyArgsThunk(pSig, m_pMethodBeingCompiled, flags); ftn = (void*)pStub->GetEntryPoint(); diff --git a/src/vm/loaderallocator.cpp b/src/vm/loaderallocator.cpp index 6b118ad92b..77f0c9e3c1 100644 --- a/src/vm/loaderallocator.cpp +++ b/src/vm/loaderallocator.cpp @@ -6,6 +6,10 @@ #include "common.h" #include "stringliteralmap.h" #include "virtualcallstub.h" +#include "threadsuspend.h" +#ifndef DACCESS_COMPILE +#include "comdelegate.h" +#endif //***************************************************************************** // Used by LoaderAllocator::Init for easier readability. @@ -43,7 +47,7 @@ LoaderAllocator::LoaderAllocator() m_cReferences = (UINT32)-1; - m_pDomainAssemblyToDelete = NULL; + m_pFirstDomainAssemblyFromSameALCToDelete = NULL; #ifdef FAT_DISPATCH_TOKENS // DispatchTokenFat pointer table for token overflow scenarios. Lazily allocated. @@ -66,6 +70,7 @@ LoaderAllocator::LoaderAllocator() m_pLastUsedCodeHeap = NULL; m_pLastUsedDynamicCodeHeap = NULL; m_pJumpStubCache = NULL; + m_IsCollectible = false; m_nLoaderAllocator = InterlockedIncrement64((LONGLONG *)&LoaderAllocator::cLoaderAllocatorsCreated); } @@ -344,15 +349,11 @@ LoaderAllocator * LoaderAllocator::GCLoaderAllocators_RemoveAssemblies(AppDomain CONTRACTL { THROWS; - GC_NOTRIGGER; // Because we are holding assembly list lock code:BaseDomain#AssemblyListLock + GC_TRIGGERS; MODE_PREEMPTIVE; SO_INTOLERANT; } CONTRACTL_END; - - _ASSERTE(pAppDomain->GetLoaderAllocatorReferencesLock()->OwnedByCurrentThread()); - _ASSERTE(pAppDomain->GetAssemblyListLock()->OwnedByCurrentThread()); - // List of LoaderAllocators being deleted LoaderAllocator * pFirstDestroyedLoaderAllocator = NULL; @@ -392,6 +393,8 @@ LoaderAllocator * LoaderAllocator::GCLoaderAllocators_RemoveAssemblies(AppDomain AppDomain::AssemblyIterator i; // Iterate through every loader allocator, marking as we go { + CrstHolder chAssemblyListLock(pAppDomain->GetAssemblyListLock()); + i = pAppDomain->IterateAssembliesEx((AssemblyIterationFlags)( kIncludeExecution | kIncludeLoaded | kIncludeCollected)); CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; @@ -416,6 +419,9 @@ LoaderAllocator * LoaderAllocator::GCLoaderAllocators_RemoveAssemblies(AppDomain // Iterate through every loader allocator, unmarking marked loaderallocators, and // build a free list of unmarked ones { + CrstHolder chLoaderAllocatorReferencesLock(pAppDomain->GetLoaderAllocatorReferencesLock()); + CrstHolder chAssemblyListLock(pAppDomain->GetAssemblyListLock()); + i = pAppDomain->IterateAssembliesEx((AssemblyIterationFlags)( kIncludeExecution | kIncludeLoaded | kIncludeCollected)); CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; @@ -436,10 +442,29 @@ LoaderAllocator * LoaderAllocator::GCLoaderAllocators_RemoveAssemblies(AppDomain } else if (!pLoaderAllocator->IsAlive()) { - pLoaderAllocator->m_pLoaderAllocatorDestroyNext = pFirstDestroyedLoaderAllocator; - // We will store a reference to this assembly, and use it later in this function - pFirstDestroyedLoaderAllocator = pLoaderAllocator; - _ASSERTE(pLoaderAllocator->m_pDomainAssemblyToDelete != NULL); + // Check that we don't have already this LoaderAllocator in the list to destroy + // (in case multiple assemblies are loaded in the same LoaderAllocator) + bool addAllocator = true; + LoaderAllocator * pCheckAllocatorToDestroy = pFirstDestroyedLoaderAllocator; + while (pCheckAllocatorToDestroy != NULL) + { + if (pCheckAllocatorToDestroy == pLoaderAllocator) + { + addAllocator = false; + break; + } + + pCheckAllocatorToDestroy = pCheckAllocatorToDestroy->m_pLoaderAllocatorDestroyNext; + } + + // Otherwise, we have a LoaderAllocator that we add to the list + if (addAllocator) + { + pLoaderAllocator->m_pLoaderAllocatorDestroyNext = pFirstDestroyedLoaderAllocator; + // We will store a reference to this assembly, and use it later in this function + pFirstDestroyedLoaderAllocator = pLoaderAllocator; + _ASSERTE(pLoaderAllocator->m_pFirstDomainAssemblyFromSameALCToDelete != NULL); + } } } } @@ -452,10 +477,27 @@ LoaderAllocator * LoaderAllocator::GCLoaderAllocators_RemoveAssemblies(AppDomain while (pDomainLoaderAllocatorDestroyIterator != NULL) { _ASSERTE(!pDomainLoaderAllocatorDestroyIterator->IsAlive()); - _ASSERTE(pDomainLoaderAllocatorDestroyIterator->m_pDomainAssemblyToDelete != NULL); - - pAppDomain->RemoveAssembly_Unlocked(pDomainLoaderAllocatorDestroyIterator->m_pDomainAssemblyToDelete); - + + DomainAssemblyIterator domainAssemblyIt(pDomainLoaderAllocatorDestroyIterator->m_pFirstDomainAssemblyFromSameALCToDelete); + + // Release all assemblies from the same ALC + while (!domainAssemblyIt.end()) + { + DomainAssembly* domainAssemblyToRemove = domainAssemblyIt; + pAppDomain->RemoveAssembly(domainAssemblyToRemove); + + if (!domainAssemblyToRemove->GetAssembly()->IsDynamic()) + { + pAppDomain->RemoveFileFromCache(domainAssemblyToRemove->GetFile()); + AssemblySpec spec; + spec.InitializeSpec(domainAssemblyToRemove->GetFile()); + VERIFY(pAppDomain->RemoveAssemblyFromCache(domainAssemblyToRemove)); + pAppDomain->RemoveNativeImageDependency(&spec); + } + + domainAssemblyIt++; + } + pDomainLoaderAllocatorDestroyIterator = pDomainLoaderAllocatorDestroyIterator->m_pLoaderAllocatorDestroyNext; } @@ -467,7 +509,7 @@ LoaderAllocator * LoaderAllocator::GCLoaderAllocators_RemoveAssemblies(AppDomain // Collect unreferenced assemblies, delete all their remaining resources. // //static -void LoaderAllocator::GCLoaderAllocators(AppDomain * pAppDomain) +void LoaderAllocator::GCLoaderAllocators(LoaderAllocator* pOriginalLoaderAllocator) { CONTRACTL { @@ -481,20 +523,16 @@ void LoaderAllocator::GCLoaderAllocators(AppDomain * pAppDomain) // List of LoaderAllocators being deleted LoaderAllocator * pFirstDestroyedLoaderAllocator = NULL; - { - CrstHolder chLoaderAllocatorReferencesLock(pAppDomain->GetLoaderAllocatorReferencesLock()); - - // We will lock the assembly list, so no other thread can delete items from it while we are deleting - // them. - // Note: Because of the previously taken lock we could just lock during every enumeration, but this - // is more robust for the future. - // This lock switches thread to GC_NOTRIGGER (see code:BaseDomain#AssemblyListLock). - CrstHolder chAssemblyListLock(pAppDomain->GetAssemblyListLock()); - - pFirstDestroyedLoaderAllocator = GCLoaderAllocators_RemoveAssemblies(pAppDomain); - } + AppDomain* pAppDomain = (AppDomain*)pOriginalLoaderAllocator->GetDomain(); + + // Collect all LoaderAllocators that don't have anymore DomainAssemblies alive + // Note: that it may not collect our pOriginalLoaderAllocator in case this + // LoaderAllocator hasn't loaded any DomainAssembly. We handle this case in the next loop. // Note: The removed LoaderAllocators are not reachable outside of this function anymore, because we // removed them from the assembly list + pFirstDestroyedLoaderAllocator = GCLoaderAllocators_RemoveAssemblies(pAppDomain); + + bool isOriginalLoaderAllocatorFound = false; // Iterate through free list, firing ETW events and notifying the debugger LoaderAllocator * pDomainLoaderAllocatorDestroyIterator = pFirstDestroyedLoaderAllocator; @@ -507,27 +545,90 @@ void LoaderAllocator::GCLoaderAllocators(AppDomain * pAppDomain) // Set the unloaded flag before notifying the debugger pDomainLoaderAllocatorDestroyIterator->SetIsUnloaded(); - DomainAssembly * pDomainAssembly = pDomainLoaderAllocatorDestroyIterator->m_pDomainAssemblyToDelete; - _ASSERTE(pDomainAssembly != NULL); - // Notify the debugger - pDomainAssembly->NotifyDebuggerUnload(); - + DomainAssemblyIterator domainAssemblyIt(pDomainLoaderAllocatorDestroyIterator->m_pFirstDomainAssemblyFromSameALCToDelete); + while (!domainAssemblyIt.end()) + { + // Notify the debugger + domainAssemblyIt->NotifyDebuggerUnload(); + domainAssemblyIt++; + } + + if (pDomainLoaderAllocatorDestroyIterator == pOriginalLoaderAllocator) + { + isOriginalLoaderAllocatorFound = true; + } pDomainLoaderAllocatorDestroyIterator = pDomainLoaderAllocatorDestroyIterator->m_pLoaderAllocatorDestroyNext; } + + // If the original LoaderAllocator was not processed, it is most likely a LoaderAllocator without any loaded DomainAssembly + // But we still want to collect it so we add it to the list of LoaderAllocator to destroy + if (!isOriginalLoaderAllocatorFound && !pOriginalLoaderAllocator->IsAlive()) + { + pOriginalLoaderAllocator->m_pLoaderAllocatorDestroyNext = pFirstDestroyedLoaderAllocator; + pFirstDestroyedLoaderAllocator = pOriginalLoaderAllocator; + } // Iterate through free list, deleting DomainAssemblies pDomainLoaderAllocatorDestroyIterator = pFirstDestroyedLoaderAllocator; while (pDomainLoaderAllocatorDestroyIterator != NULL) { _ASSERTE(!pDomainLoaderAllocatorDestroyIterator->IsAlive()); - _ASSERTE(pDomainLoaderAllocatorDestroyIterator->m_pDomainAssemblyToDelete != NULL); - - delete pDomainLoaderAllocatorDestroyIterator->m_pDomainAssemblyToDelete; + + DomainAssemblyIterator domainAssemblyIt(pDomainLoaderAllocatorDestroyIterator->m_pFirstDomainAssemblyFromSameALCToDelete); + while (!domainAssemblyIt.end()) + { + delete (DomainAssembly*)domainAssemblyIt; + domainAssemblyIt++; + } // We really don't have to set it to NULL as the assembly is not reachable anymore, but just in case ... // (Also debugging NULL AVs if someone uses it accidentally is so much easier) - pDomainLoaderAllocatorDestroyIterator->m_pDomainAssemblyToDelete = NULL; - - pDomainLoaderAllocatorDestroyIterator = pDomainLoaderAllocatorDestroyIterator->m_pLoaderAllocatorDestroyNext; + pDomainLoaderAllocatorDestroyIterator->m_pFirstDomainAssemblyFromSameALCToDelete = NULL; + + pDomainLoaderAllocatorDestroyIterator->ReleaseManagedAssemblyLoadContext(); + + // The following code was previously happening on delete ~DomainAssembly->Terminate + // We are moving this part here in order to make sure that we can unload a LoaderAllocator + // that didn't have a DomainAssembly + // (we have now a LoaderAllocator with 0-n DomainAssembly) + + // This cleanup code starts resembling parts of AppDomain::Terminate too much. + // It would be useful to reduce duplication and also establish clear responsibilites + // for LoaderAllocator::Destroy, Assembly::Terminate, LoaderAllocator::Terminate + // and LoaderAllocator::~LoaderAllocator. We need to establish how these + // cleanup paths interact with app-domain unload and process tear-down, too. + + if (!IsAtProcessExit()) + { + // Suspend the EE to do some clean up that can only occur + // while no threads are running. + GCX_COOP(); // SuspendEE may require current thread to be in Coop mode + // SuspendEE cares about the reason flag only when invoked for a GC + // Other values are typically ignored. If using SUSPEND_FOR_APPDOMAIN_SHUTDOWN + // is inappropriate, we can introduce a new flag or hijack an unused one. + ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_APPDOMAIN_SHUTDOWN); + } + + ExecutionManager::Unload(pDomainLoaderAllocatorDestroyIterator); + pDomainLoaderAllocatorDestroyIterator->UninitVirtualCallStubManager(); + + // TODO: Do we really want to perform this on each LoaderAllocator? + MethodTable::ClearMethodDataCache(); + ClearJitGenericHandleCache(pAppDomain); + + if (!IsAtProcessExit()) + { + // Resume the EE. + ThreadSuspend::RestartEE(FALSE, TRUE); + } + + // Because RegisterLoaderAllocatorForDeletion is modifying m_pLoaderAllocatorDestroyNext, we are saving it here + LoaderAllocator* pLoaderAllocatorDestroyNext = pDomainLoaderAllocatorDestroyIterator->m_pLoaderAllocatorDestroyNext; + + // Register this LoaderAllocator for cleanup + pAppDomain->RegisterLoaderAllocatorForDeletion(pDomainLoaderAllocatorDestroyIterator); + + // Go to next + pDomainLoaderAllocatorDestroyIterator = pLoaderAllocatorDestroyNext; } // Deleting the DomainAssemblies will have created a list of LoaderAllocator's on the AppDomain @@ -554,19 +655,20 @@ BOOL QCALLTYPE LoaderAllocator::Destroy(QCall::LoaderAllocatorHandle pLoaderAllo // This will probably change for shared code unloading _ASSERTE(pID->GetType() == LAT_Assembly); - Assembly *pAssembly = pID->GetDomainAssembly()->GetCurrentAssembly(); - - //if not fully loaded, it is still domain specific, so just get one from DomainAssembly - BaseDomain *pDomain = pAssembly ? pAssembly->Parent() : pID->GetDomainAssembly()->GetAppDomain(); - - pLoaderAllocator->CleanupStringLiteralMap(); + DomainAssembly* pDomainAssembly = (DomainAssembly*)(pID->GetDomainAssemblyIterator()); + if (pDomainAssembly != NULL) + { + Assembly *pAssembly = pDomainAssembly->GetCurrentAssembly(); - // This will probably change for shared code unloading - _ASSERTE(pDomain->IsAppDomain()); + //if not fully loaded, it is still domain specific, so just get one from DomainAssembly + BaseDomain *pDomain = pAssembly ? pAssembly->Parent() : pDomainAssembly->GetAppDomain(); - AppDomain *pAppDomain = pDomain->AsAppDomain(); + // This will probably change for shared code unloading + _ASSERTE(pDomain->IsAppDomain()); - pLoaderAllocator->m_pDomainAssemblyToDelete = pAssembly->GetDomainAssembly(pAppDomain); + AppDomain *pAppDomain = pDomain->AsAppDomain(); + pLoaderAllocator->m_pFirstDomainAssemblyFromSameALCToDelete = pAssembly->GetDomainAssembly(pAppDomain); + } // Iterate through all references to other loader allocators and decrement their reference // count @@ -587,7 +689,7 @@ BOOL QCALLTYPE LoaderAllocator::Destroy(QCall::LoaderAllocatorHandle pLoaderAllo // may hit zero early. if (fIsLastReferenceReleased) { - LoaderAllocator::GCLoaderAllocators(pAppDomain); + LoaderAllocator::GCLoaderAllocators(pLoaderAllocator); } STRESS_LOG1(LF_CLASSLOADER, LL_INFO100, "End LoaderAllocator::Destroy for loader allocator %p\n", reinterpret_cast<void *>(static_cast<PTR_LoaderAllocator>(pLoaderAllocator))); @@ -1217,6 +1319,8 @@ void LoaderAllocator::Terminate() } #endif // FAT_DISPATCH_TOKENS + CleanupStringLiteralMap(); + LOG((LF_CLASSLOADER, LL_INFO100, "End LoaderAllocator::Terminate for loader allocator %p\n", reinterpret_cast<void *>(static_cast<PTR_LoaderAllocator>(this)))); } @@ -1400,7 +1504,7 @@ DispatchToken LoaderAllocator::TryLookupDispatchToken(UINT32 typeId, UINT32 slot } } -void LoaderAllocator::InitVirtualCallStubManager(BaseDomain * pDomain, BOOL fCollectible /* = FALSE */) +void LoaderAllocator::InitVirtualCallStubManager(BaseDomain * pDomain) { STANDARD_VM_CONTRACT; @@ -1468,9 +1572,56 @@ BOOL LoaderAllocator::IsDomainNeutral() return GetDomain()->IsSharedDomain(); } +DomainAssemblyIterator::DomainAssemblyIterator(DomainAssembly* pFirstAssembly) +{ + pCurrentAssembly = pFirstAssembly; + pNextAssembly = pCurrentAssembly ? pCurrentAssembly->GetNextDomainAssemblyInSameALC() : NULL; +} + +void DomainAssemblyIterator::operator++() +{ + pCurrentAssembly = pNextAssembly; + pNextAssembly = pCurrentAssembly ? pCurrentAssembly->GetNextDomainAssemblyInSameALC() : NULL; +} + +void AssemblyLoaderAllocator::SetCollectible() +{ + CONTRACTL + { + THROWS; + } + CONTRACTL_END; + + m_IsCollectible = true; +#ifndef DACCESS_COMPILE + m_pShuffleThunkCache = new ShuffleThunkCache(m_pStubHeap); +#endif +} + #ifndef DACCESS_COMPILE #ifndef CROSSGEN_COMPILE + +AssemblyLoaderAllocator::~AssemblyLoaderAllocator() +{ + if (m_binderToRelease != NULL) + { + VERIFY(m_binderToRelease->Release() == 0); + m_binderToRelease = NULL; + } + + delete m_pShuffleThunkCache; + m_pShuffleThunkCache = NULL; +} + +void AssemblyLoaderAllocator::RegisterBinder(CLRPrivBinderAssemblyLoadContext* binderToRelease) +{ + // When the binder is registered it will be released by the destructor + // of this instance + _ASSERTE(m_binderToRelease == NULL); + m_binderToRelease = binderToRelease; +} + STRINGREF *LoaderAllocator::GetStringObjRefPtrFromUnicodeString(EEStringData *pStringData) { CONTRACTL @@ -1666,6 +1817,25 @@ void LoaderAllocator::CleanupFailedTypeInit() pLock->Unlink(pItem->m_pListLockEntry); } } + +void AssemblyLoaderAllocator::ReleaseManagedAssemblyLoadContext() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + SO_INTOLERANT; + } + CONTRACTL_END; + + if (m_binderToRelease != NULL) + { + // Release the managed ALC + m_binderToRelease->ReleaseLoadContext(); + } +} + #endif // !CROSSGEN_COMPILE #endif // !DACCESS_COMPILE diff --git a/src/vm/loaderallocator.hpp b/src/vm/loaderallocator.hpp index b057283136..abfd4d0c45 100644 --- a/src/vm/loaderallocator.hpp +++ b/src/vm/loaderallocator.hpp @@ -29,6 +29,40 @@ enum LoaderAllocatorType LAT_Assembly }; +class CLRPrivBinderAssemblyLoadContext; + +// Iterator over a DomainAssembly in the same ALC +class DomainAssemblyIterator +{ + DomainAssembly* pCurrentAssembly; + DomainAssembly* pNextAssembly; + +public: + DomainAssemblyIterator(DomainAssembly* pFirstAssembly); + + bool end() const + { + return pCurrentAssembly == NULL; + } + + operator DomainAssembly*() const + { + return pCurrentAssembly; + } + + DomainAssembly* operator ->() const + { + return pCurrentAssembly; + } + + void operator++(); + + void operator++(int dummy) + { + this->operator++(); + } +}; + class LoaderAllocatorID { @@ -52,12 +86,11 @@ public: VOID Init(); VOID Init(AppDomain* pAppDomain); LoaderAllocatorType GetType(); - VOID SetDomainAssembly(DomainAssembly* pDomainAssembly); - DomainAssembly* GetDomainAssembly(); + VOID AddDomainAssembly(DomainAssembly* pDomainAssembly); + DomainAssemblyIterator GetDomainAssemblyIterator(); AppDomain* GetAppDomain(); BOOL Equals(LoaderAllocatorID* pId); COUNT_T Hash(); - BOOL IsCollectible(); }; class StringLiteralMap; @@ -101,6 +134,7 @@ protected: bool m_fTerminated; bool m_fMarked; int m_nGCCount; + bool m_IsCollectible; // Pre-allocated blocks of heap for collectible assemblies. Will be set to NULL as soon as it is // used. See code in GetVSDHeapInitialBlock and GetCodeHeapInitialBlock @@ -156,7 +190,7 @@ private: Volatile<UINT32> m_cReferences; // This will be set by code:LoaderAllocator::Destroy (from managed scout finalizer) and signalizes that // the assembly was collected - DomainAssembly * m_pDomainAssemblyToDelete; + DomainAssembly * m_pFirstDomainAssemblyFromSameALCToDelete; BOOL CheckAddReference_Unlocked(LoaderAllocator *pOtherLA); @@ -271,11 +305,11 @@ public: // Checks if managed scout is alive - see code:#AssemblyPhases. BOOL IsManagedScoutAlive() { - return (m_pDomainAssemblyToDelete == NULL); + return (m_pFirstDomainAssemblyFromSameALCToDelete == NULL); } // Collect unreferenced assemblies, delete all their remaining resources. - static void GCLoaderAllocators(AppDomain *pAppDomain); + static void GCLoaderAllocators(LoaderAllocator* firstLoaderAllocator); UINT64 GetCreationNumber() { LIMITED_METHOD_DAC_CONTRACT; return m_nLoaderAllocator; } @@ -298,7 +332,7 @@ public: DispatchToken TryLookupDispatchToken(UINT32 typeId, UINT32 slotNumber); virtual LoaderAllocatorID* Id() =0; - BOOL IsCollectible() { WRAPPER_NO_CONTRACT; return Id()->IsCollectible(); } + BOOL IsCollectible() { WRAPPER_NO_CONTRACT; return m_IsCollectible; } #ifdef DACCESS_COMPILE void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); @@ -400,6 +434,8 @@ public: BOOL IsDomainNeutral(); void Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory = NULL); void Terminate(); + virtual void ReleaseManagedAssemblyLoadContext() {} + SIZE_T EstimateSize(); void SetupManagedTracking(LOADERALLOCATORREF *pLoaderAllocatorKeepAlive); @@ -433,7 +469,7 @@ public: STRINGREF *GetOrInternString(STRINGREF *pString); void CleanupStringLiteralMap(); - void InitVirtualCallStubManager(BaseDomain *pDomain, BOOL fCollectible = FALSE); + void InitVirtualCallStubManager(BaseDomain *pDomain); void UninitVirtualCallStubManager(); #ifndef CROSSGEN_COMPILE inline VirtualCallStubManager *GetVirtualCallStubManager() @@ -449,7 +485,7 @@ typedef VPTR(LoaderAllocator) PTR_LoaderAllocator; class GlobalLoaderAllocator : public LoaderAllocator { VPTR_VTABLE_CLASS(GlobalLoaderAllocator, LoaderAllocator) - VPTR_UNIQUE(VPTRU_LoaderAllocator+1); + VPTR_UNIQUE(VPTRU_LoaderAllocator+1) BYTE m_ExecutableHeapInstance[sizeof(LoaderHeap)]; @@ -469,7 +505,7 @@ typedef VPTR(GlobalLoaderAllocator) PTR_GlobalLoaderAllocator; class AppDomainLoaderAllocator : public LoaderAllocator { VPTR_VTABLE_CLASS(AppDomainLoaderAllocator, LoaderAllocator) - VPTR_UNIQUE(VPTRU_LoaderAllocator+2); + VPTR_UNIQUE(VPTRU_LoaderAllocator+2) protected: LoaderAllocatorID m_Id; @@ -482,23 +518,49 @@ public: typedef VPTR(AppDomainLoaderAllocator) PTR_AppDomainLoaderAllocator; +class ShuffleThunkCache; + class AssemblyLoaderAllocator : public LoaderAllocator { VPTR_VTABLE_CLASS(AssemblyLoaderAllocator, LoaderAllocator) - VPTR_UNIQUE(VPTRU_LoaderAllocator+3); + VPTR_UNIQUE(VPTRU_LoaderAllocator+3) protected: - LoaderAllocatorID m_Id; + LoaderAllocatorID m_Id; + ShuffleThunkCache* m_pShuffleThunkCache; public: virtual LoaderAllocatorID* Id(); - AssemblyLoaderAllocator() : m_Id(LAT_Assembly) { LIMITED_METHOD_CONTRACT; } + AssemblyLoaderAllocator() : m_Id(LAT_Assembly), m_pShuffleThunkCache(NULL) +#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + , m_binderToRelease(NULL) +#endif + { LIMITED_METHOD_CONTRACT; } void Init(AppDomain *pAppDomain); virtual BOOL CanUnload(); - void SetDomainAssembly(DomainAssembly *pDomainAssembly) { WRAPPER_NO_CONTRACT; m_Id.SetDomainAssembly(pDomainAssembly); } + + void SetCollectible(); + + void AddDomainAssembly(DomainAssembly *pDomainAssembly) + { + WRAPPER_NO_CONTRACT; + m_Id.AddDomainAssembly(pDomainAssembly); + } + + ShuffleThunkCache* GetShuffleThunkCache() + { + return m_pShuffleThunkCache; + } #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) virtual void RegisterHandleForCleanup(OBJECTHANDLE objHandle); virtual void CleanupHandles(); + CLRPrivBinderAssemblyLoadContext* GetBinder() + { + return m_binderToRelease; + } + virtual ~AssemblyLoaderAllocator(); + void RegisterBinder(CLRPrivBinderAssemblyLoadContext* binderToRelease); + virtual void ReleaseManagedAssemblyLoadContext(); #endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) private: @@ -514,6 +576,9 @@ private: }; SList<HandleCleanupListItem> m_handleCleanupList; +#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + CLRPrivBinderAssemblyLoadContext* m_binderToRelease; +#endif }; typedef VPTR(AssemblyLoaderAllocator) PTR_AssemblyLoaderAllocator; diff --git a/src/vm/loaderallocator.inl b/src/vm/loaderallocator.inl index 3f23ac9c8c..46c253f4bd 100644 --- a/src/vm/loaderallocator.inl +++ b/src/vm/loaderallocator.inl @@ -56,51 +56,51 @@ inline void LoaderAllocatorID::Init() m_type = LAT_Assembly; }; -inline void LoaderAllocatorID::SetDomainAssembly(DomainAssembly* pAssembly) +inline void LoaderAllocatorID::AddDomainAssembly(DomainAssembly* pAssembly) { LIMITED_METHOD_CONTRACT; _ASSERTE(m_type == LAT_Assembly); + + // Link domain assembly together + if (m_pDomainAssembly != NULL) + { + pAssembly->SetNextDomainAssemblyInSameALC(m_pDomainAssembly); + } m_pDomainAssembly = pAssembly; } inline VOID* LoaderAllocatorID::GetValue() { - LIMITED_METHOD_DAC_CONTRACT; + LIMITED_METHOD_DAC_CONTRACT; return m_pValue; } inline COUNT_T LoaderAllocatorID::Hash() { - LIMITED_METHOD_DAC_CONTRACT; + LIMITED_METHOD_DAC_CONTRACT; return (COUNT_T)(SIZE_T)GetValue(); } inline LoaderAllocatorType LoaderAllocatorID::GetType() { - LIMITED_METHOD_DAC_CONTRACT; + LIMITED_METHOD_DAC_CONTRACT; return m_type; } -inline DomainAssembly* LoaderAllocatorID::GetDomainAssembly() +inline DomainAssemblyIterator LoaderAllocatorID::GetDomainAssemblyIterator() { - LIMITED_METHOD_DAC_CONTRACT; + LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(m_type == LAT_Assembly); - return m_pDomainAssembly; + return DomainAssemblyIterator(m_pDomainAssembly); } inline AppDomain *LoaderAllocatorID::GetAppDomain() { - LIMITED_METHOD_DAC_CONTRACT; + LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(m_type == LAT_AppDomain); return m_pAppDomain; } -inline BOOL LoaderAllocatorID::IsCollectible() -{ - LIMITED_METHOD_DAC_CONTRACT; - return m_type == LAT_Assembly; -} - inline LoaderAllocatorID* AssemblyLoaderAllocator::Id() { LIMITED_METHOD_DAC_CONTRACT; diff --git a/src/vm/methodtable.inl b/src/vm/methodtable.inl index 9e5df0262c..f669f23a98 100644 --- a/src/vm/methodtable.inl +++ b/src/vm/methodtable.inl @@ -197,7 +197,7 @@ inline void MethodTable::SetLoaderAllocator(LoaderAllocator* pAllocator) LIMITED_METHOD_CONTRACT; _ASSERTE(pAllocator == GetLoaderAllocator()); - if (pAllocator->Id()->IsCollectible()) + if (pAllocator->IsCollectible()) { SetFlag(enum_flag_Collectible); } diff --git a/src/vm/methodtablebuilder.cpp b/src/vm/methodtablebuilder.cpp index ef9c37c5d5..08474ad3cf 100644 --- a/src/vm/methodtablebuilder.cpp +++ b/src/vm/methodtablebuilder.cpp @@ -1723,7 +1723,7 @@ MethodTableBuilder::BuildMethodTableThrowing( // the offsets of our fields will depend on this. For the dynamic case (which requires // an extra indirection (indirect depending of methodtable) we'll allocate the slot // in setupmethodtable - if (((pModule->IsReflection() || bmtGenerics->HasInstantiation() || !pModule->IsStaticStoragePrepared(cl)) && + if (((pAllocator->IsCollectible() || pModule->IsReflection() || bmtGenerics->HasInstantiation() || !pModule->IsStaticStoragePrepared(cl)) && (bmtVT->GetClassCtorSlotIndex() != INVALID_SLOT_INDEX || bmtEnumFields->dwNumStaticFields !=0)) #ifdef EnC_SUPPORTED // Classes in modules that have been edited (would do on class level if there were a @@ -3187,12 +3187,6 @@ MethodTableBuilder::EnumerateClassMethods() type = METHOD_TYPE_NORMAL; } - // PInvoke methods are not permitted on collectible types - if ((type == METHOD_TYPE_NDIRECT) && GetAssembly()->IsCollectible()) - { - BuildMethodTableThrowException(IDS_CLASSLOAD_COLLECTIBLEPINVOKE); - } - // Generic methods should always be METHOD_TYPE_INSTANTIATED if ((numGenericMethodArgs != 0) && (type != METHOD_TYPE_INSTANTIATED)) { diff --git a/src/vm/stublink.cpp b/src/vm/stublink.cpp index 631f51150b..d9715b7630 100644 --- a/src/vm/stublink.cpp +++ b/src/vm/stublink.cpp @@ -195,7 +195,7 @@ FindStubFunctionEntry ( } -void UnregisterUnwindInfoInLoaderHeapCallback (PVOID pvAllocationBase, SIZE_T cbReserved) +bool UnregisterUnwindInfoInLoaderHeapCallback (PVOID pvArgs, PVOID pvAllocationBase, SIZE_T cbReserved) { CONTRACTL { @@ -251,6 +251,8 @@ void UnregisterUnwindInfoInLoaderHeapCallback (PVOID pvAllocationBase, SIZE_T cb ppPrevStubHeapSegment = &pStubHeapSegment->pNext; } } + + return false; // Keep enumerating } @@ -264,7 +266,7 @@ VOID UnregisterUnwindInfoInLoaderHeap (UnlockedLoaderHeap *pHeap) } CONTRACTL_END; - pHeap->EnumPageRegions(&UnregisterUnwindInfoInLoaderHeapCallback); + pHeap->EnumPageRegions(&UnregisterUnwindInfoInLoaderHeapCallback, NULL /* pvArgs */); #ifdef _DEBUG pHeap->m_fStubUnwindInfoUnregistered = TRUE; @@ -854,7 +856,7 @@ Stub *StubLinker::LinkInterceptor(LoaderHeap *pHeap, Stub* interceptee, void *pR , UnwindInfoSize(globalsize) #endif ); - bool fSuccess; fSuccess = EmitStub(pStub, globalsize); + bool fSuccess; fSuccess = EmitStub(pStub, globalsize, pHeap); #ifdef STUBLINKER_GENERATES_UNWIND_INFO if (fSuccess) @@ -910,7 +912,7 @@ Stub *StubLinker::Link(LoaderHeap *pHeap, DWORD flags) ); ASSERT(pStub != NULL); - bool fSuccess; fSuccess = EmitStub(pStub, globalsize); + bool fSuccess; fSuccess = EmitStub(pStub, globalsize, pHeap); #ifdef STUBLINKER_GENERATES_UNWIND_INFO if (fSuccess) @@ -1072,7 +1074,7 @@ int StubLinker::CalculateSize(int* pGlobalSize) return globalsize + datasize; } -bool StubLinker::EmitStub(Stub* pStub, int globalsize) +bool StubLinker::EmitStub(Stub* pStub, int globalsize, LoaderHeap* pHeap) { STANDARD_VM_CONTRACT; @@ -1157,7 +1159,7 @@ bool StubLinker::EmitStub(Stub* pStub, int globalsize) #ifdef STUBLINKER_GENERATES_UNWIND_INFO if (pStub->HasUnwindInfo()) { - if (!EmitUnwindInfo(pStub, globalsize)) + if (!EmitUnwindInfo(pStub, globalsize, pHeap)) return false; } #endif // STUBLINKER_GENERATES_UNWIND_INFO @@ -1308,7 +1310,34 @@ UNWIND_CODE *StubLinker::AllocUnwindInfo (UCHAR Op, UCHAR nExtraSlots /*= 0*/) } #endif // defined(_TARGET_AMD64_) -bool StubLinker::EmitUnwindInfo(Stub* pStub, int globalsize) +struct FindBlockArgs +{ + BYTE *pCode; + BYTE *pBlockBase; + SIZE_T cbBlockSize; +}; + +bool FindBlockCallback (PTR_VOID pvArgs, PTR_VOID pvAllocationBase, SIZE_T cbReserved) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + } + CONTRACTL_END; + + FindBlockArgs* pArgs = (FindBlockArgs*)pvArgs; + if (pArgs->pCode >= pvAllocationBase && (pArgs->pCode < ((BYTE *)pvAllocationBase + cbReserved))) + { + pArgs->pBlockBase = (BYTE*)pvAllocationBase; + pArgs->cbBlockSize = cbReserved; + return true; + } + + return false; +} + +bool StubLinker::EmitUnwindInfo(Stub* pStub, int globalsize, LoaderHeap* pHeap) { STANDARD_VM_CONTRACT; @@ -1316,19 +1345,21 @@ bool StubLinker::EmitUnwindInfo(Stub* pStub, int globalsize) // // Determine the lower bound of the address space containing the stub. - // The properties of individual pages may change, but the bounds of a - // VirtualAlloc(MEM_RESERVE)'d region will never change. // - MEMORY_BASIC_INFORMATION mbi; + FindBlockArgs findBlockArgs; + findBlockArgs.pCode = pCode; + findBlockArgs.pBlockBase = NULL; + + pHeap->EnumPageRegions(&FindBlockCallback, &findBlockArgs); - if (sizeof(mbi) != ClrVirtualQuery(pCode, &mbi, sizeof(mbi))) + if (findBlockArgs.pBlockBase == NULL) { // REVISIT_TODO better exception COMPlusThrowOM(); } - BYTE *pbRegionBaseAddress = (BYTE*)mbi.AllocationBase; + BYTE *pbRegionBaseAddress = findBlockArgs.pBlockBase; #ifdef _DEBUG static SIZE_T MaxSegmentSize = -1; @@ -1805,39 +1836,12 @@ bool StubLinker::EmitUnwindInfo(Stub* pStub, int globalsize) if (!pStubHeapSegment) { // - // Determine the upper bound of the address space containing the stub. - // Start with stub region's allocation base, and query for each - // successive region's allocation base until it changes or we hit an - // unreserved region. - // - - PBYTE pbCurrentBase = pbBaseAddress; - - for (;;) - { - if (sizeof(mbi) != ClrVirtualQuery(pbCurrentBase, &mbi, sizeof(mbi))) - { - // REVISIT_TODO better exception - COMPlusThrowOM(); - } - - // AllocationBase is undefined if this is set. - if (mbi.State & MEM_FREE) - break; - - if (pbRegionBaseAddress != mbi.AllocationBase) - break; - - pbCurrentBase += mbi.RegionSize; - } - - // // RtlInstallFunctionTableCallback will only accept a ULONG for the // region size. We've already checked above that the RUNTIME_FUNCTION // offsets will work relative to pbBaseAddress. // - SIZE_T cbSegment = pbCurrentBase - pbBaseAddress; + SIZE_T cbSegment = findBlockArgs.cbBlockSize; if (cbSegment > MaxSegmentSize) cbSegment = MaxSegmentSize; diff --git a/src/vm/stublink.h b/src/vm/stublink.h index d7f0034587..41c11ebb49 100644 --- a/src/vm/stublink.h +++ b/src/vm/stublink.h @@ -281,7 +281,6 @@ public: // // Throws exception on failure. //--------------------------------------------------------------- - Stub *Link(DWORD flags = 0) { WRAPPER_NO_CONTRACT; return Link(NULL, flags); } Stub *Link(LoaderHeap *heap, DWORD flags = 0); //--------------------------------------------------------------- @@ -411,11 +410,11 @@ private: // Writes out the code element into memory following the // stub object. - bool EmitStub(Stub* pStub, int globalsize); + bool EmitStub(Stub* pStub, int globalsize, LoaderHeap* pHeap); CodeRun *GetLastCodeRunIfAny(); - bool EmitUnwindInfo(Stub* pStub, int globalsize); + bool EmitUnwindInfo(Stub* pStub, int globalsize, LoaderHeap* pHeap); #if defined(_TARGET_AMD64_) && defined(STUBLINKER_GENERATES_UNWIND_INFO) UNWIND_CODE *AllocUnwindInfo (UCHAR Op, UCHAR nExtraSlots = 0); diff --git a/tests/src/GC/API/GCHandle/Casting.cs b/tests/src/GC/API/GCHandle/Casting.cs index 94f8c40e13..b755804c05 100644 --- a/tests/src/GC/API/GCHandle/Casting.cs +++ b/tests/src/GC/API/GCHandle/Casting.cs @@ -33,7 +33,12 @@ public class CastingTest GCHandle gch = GCHandle.Alloc(new Dummy(dummyValue)); GCHandle gch2 = (GCHandle)((IntPtr)gch); - if (gch.Target == gch2.Target) + + bool success = (gch.Target == gch2.Target); + + gch.Free(); + + if (success) { Console.WriteLine("CastTest Passed"); return true; diff --git a/tests/src/GC/API/GCHandle/Normal.cs b/tests/src/GC/API/GCHandle/Normal.cs index ffa7eaa6cb..4d36e481d7 100644 --- a/tests/src/GC/API/GCHandle/Normal.cs +++ b/tests/src/GC/API/GCHandle/Normal.cs @@ -21,7 +21,7 @@ public class Test { } [MethodImplAttribute(MethodImplOptions.NoInlining)] - public static void RunTest() + public static GCHandle RunTest() { Dummy obj = new Dummy(); @@ -30,17 +30,23 @@ public class Test { // ensuring that GC happens even with /debug mode obj=null; + + return handle; } public static int Main() { - RunTest(); + GCHandle handle = RunTest(); GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); - if(Dummy.flag == 0) { + bool success = (Dummy.flag == 0); + + handle.Free(); + + if (success) { Console.WriteLine("Test for GCHandleType.Normal passed!"); return 100; } diff --git a/tests/src/GC/API/GCHandle/Target.cs b/tests/src/GC/API/GCHandle/Target.cs index 7f42cfef6a..17b157c6db 100644 --- a/tests/src/GC/API/GCHandle/Target.cs +++ b/tests/src/GC/API/GCHandle/Target.cs @@ -57,6 +57,8 @@ public class Test passed = false; } + handle.Free(); + if (passed) { Console.WriteLine("Test Passed!"); diff --git a/tests/src/GC/API/GCHandle/ToFromIntPtr.cs b/tests/src/GC/API/GCHandle/ToFromIntPtr.cs index 520ca4db49..18fdb03730 100644 --- a/tests/src/GC/API/GCHandle/ToFromIntPtr.cs +++ b/tests/src/GC/API/GCHandle/ToFromIntPtr.cs @@ -34,7 +34,11 @@ public class ToFromIntPtrTest GCHandle gch = GCHandle.Alloc(new Dummy(dummyValue)); GCHandle gch2 = GCHandle.FromIntPtr(GCHandle.ToIntPtr(gch)); - if (gch.Target == gch2.Target) + bool success = (gch.Target == gch2.Target); + + gch.Free(); + + if (success) { Console.WriteLine("ToFromTest Passed"); return true; diff --git a/tests/src/GC/API/WeakReference/Finalize.cs b/tests/src/GC/API/WeakReference/Finalize.cs index aa541e819c..1e1a2d630c 100644 --- a/tests/src/GC/API/WeakReference/Finalize.cs +++ b/tests/src/GC/API/WeakReference/Finalize.cs @@ -33,7 +33,7 @@ public class Test { } [MethodImplAttribute(MethodImplOptions.NoInlining)] - public void RunTest() + public GCHandle RunTest() { WeakReference weak1 = new WeakReference(dummy1); GCHandle handle = GCHandle.Alloc(dummy1,GCHandleType.Normal); // Strong Reference @@ -43,19 +43,25 @@ public class Test { // ensuring that GC happens even with /debug mode dummy1=null; dummy2=null; + + return handle; } } public static int Main() { CreateObj temp = new CreateObj(); - temp.RunTest(); + GCHandle handle = temp.RunTest(); GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); - if (Dummy.visited) + bool success = Dummy.visited; + + handle.Free(); + + if (success) { Console.WriteLine("Test for WeakReference.Finalize() passed!"); return 100; diff --git a/tests/src/JIT/Directed/nullabletypes/Desktop/StructDefinitions.cs b/tests/src/JIT/Directed/nullabletypes/Desktop/StructDefinitions.cs index 66bd192904..9c79297e3a 100644 --- a/tests/src/JIT/Directed/nullabletypes/Desktop/StructDefinitions.cs +++ b/tests/src/JIT/Directed/nullabletypes/Desktop/StructDefinitions.cs @@ -2,10 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; using System; using System.ComponentModel; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Loader; #pragma warning disable 0183 @@ -227,13 +229,25 @@ public sealed class SealedClass { } public delegate void SimpleDelegate(); public delegate void GenericDelegate<T>(); - +// Create Value Instance internal static class Helper { public static GCHandle GCHANDLE; + static Helper() { GCHANDLE = GCHandle.Alloc(Console.Out); + + AssemblyLoadContext currentContext = AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()); + if (currentContext != null) + { + currentContext.Unloading += Context_Unloading; + } + } + + private static void Context_Unloading(AssemblyLoadContext obj) + { + GCHANDLE.Free(); } public static char Create(char val) { return 'c'; } diff --git a/tests/src/JIT/Regression/CLR-x86-JIT/V1-M09/b13621/b13621.cs b/tests/src/JIT/Regression/CLR-x86-JIT/V1-M09/b13621/b13621.cs index b130032013..f7dd1fe097 100644 --- a/tests/src/JIT/Regression/CLR-x86-JIT/V1-M09/b13621/b13621.cs +++ b/tests/src/JIT/Regression/CLR-x86-JIT/V1-M09/b13621/b13621.cs @@ -24,6 +24,12 @@ namespace DefaultNamespace if (n % 50 == 0) Console.Out.WriteLine(n); } + + for (n = 0; n < iSize; n++) + { + root[n].Free(); + } + return 100; } diff --git a/tests/src/JIT/jit64/valuetypes/nullable/box-unbox/structdef.cs b/tests/src/JIT/jit64/valuetypes/nullable/box-unbox/structdef.cs index 2daf10fddc..b22d01f278 100644 --- a/tests/src/JIT/jit64/valuetypes/nullable/box-unbox/structdef.cs +++ b/tests/src/JIT/jit64/valuetypes/nullable/box-unbox/structdef.cs @@ -4,10 +4,11 @@ #pragma warning disable 0183 // The given expression is always of the provided ('X') type -using System.Runtime.InteropServices; using System; -//using Microsoft; using System.ComponentModel; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Loader; // primitives / CLR Types // interfaces @@ -223,14 +224,25 @@ public static class ExitCode public static int Passed = 100; } - // Create Value Instance internal static class Helper { public static GCHandle GCHANDLE; + static Helper() { - GCHANDLE = GCHandle.Alloc(System.Console.Out); + GCHANDLE = GCHandle.Alloc(Console.Out); + + AssemblyLoadContext currentContext = AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()); + if (currentContext != null) + { + currentContext.Unloading += Context_Unloading; + } + } + + private static void Context_Unloading(AssemblyLoadContext obj) + { + GCHANDLE.Free(); } public static char Create(char val) { return 'c'; } diff --git a/tests/src/JIT/jit64/valuetypes/nullable/castclass/structdef.cs b/tests/src/JIT/jit64/valuetypes/nullable/castclass/structdef.cs index 879993bb6d..64363ad01a 100644 --- a/tests/src/JIT/jit64/valuetypes/nullable/castclass/structdef.cs +++ b/tests/src/JIT/jit64/valuetypes/nullable/castclass/structdef.cs @@ -4,9 +4,11 @@ #pragma warning disable 0183 -using System.Runtime.InteropServices; using System; using System.ComponentModel; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Loader; // primitives / CLR Types // interfaces @@ -222,14 +224,25 @@ public static class ExitCode public static int Passed = 100; } - // Create Value Instance internal static class Helper { public static GCHandle GCHANDLE; + static Helper() { - GCHANDLE = GCHandle.Alloc(System.Console.Out); + GCHANDLE = GCHandle.Alloc(Console.Out); + + AssemblyLoadContext currentContext = AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()); + if (currentContext != null) + { + currentContext.Unloading += Context_Unloading; + } + } + + private static void Context_Unloading(AssemblyLoadContext obj) + { + GCHANDLE.Free(); } public static char Create(char val) { return 'c'; } |