summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJan Vorlicek <janvorli@microsoft.com>2018-08-23 02:42:43 +0200
committerGitHub <noreply@github.com>2018-08-23 02:42:43 +0200
commit8cd4b39a42c1c7cf37502357e6a4cb2888f5dfd7 (patch)
treea767facb00becfc9b6b9f0c3a8d731e566e3d1c3 /src
parent8013a4b6786d9e6618f8ed36bd0a473aa17b72aa (diff)
downloadcoreclr-8cd4b39a42c1c7cf37502357e6a4cb2888f5dfd7.tar.gz
coreclr-8cd4b39a42c1c7cf37502357e6a4cb2888f5dfd7.tar.bz2
coreclr-8cd4b39a42c1c7cf37502357e6a4cb2888f5dfd7.zip
Enable unloading of AssemblyLoadContext (#18476)
Enable assembly unloading * Allow PInvoke methods on collectible assemblies * Fix test unloadability Several hundreds of tests were using Helper class that created GCHandle, but never freed it. That prevented unloading of those tests. The change modifies the Helper class to keep the handle in a finalizable object. Several GCHandle related tests were not freeing the GCHandle they allocated, so this change adds freeing them to enable the unloading. * Add missing error messages to the resources * Fix shuffle thunk cache for unloadability * Add GetLoaderAllocator to ICLRPrivBinder
Diffstat (limited to 'src')
-rw-r--r--src/System.Private.CoreLib/Resources/Strings.resx12
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs261
-rw-r--r--src/binder/assembly.cpp5
-rw-r--r--src/binder/clrprivbinderassemblyloadcontext.cpp74
-rw-r--r--src/binder/clrprivbindercoreclr.cpp6
-rw-r--r--src/binder/inc/assembly.hpp12
-rw-r--r--src/binder/inc/clrprivbinderassemblyloadcontext.h28
-rw-r--r--src/binder/inc/clrprivbindercoreclr.h3
-rw-r--r--src/dlls/mscorrc/mscorrc.rc1
-rw-r--r--src/dlls/mscorrc/resource.h1
-rw-r--r--src/inc/clrprivbinderutil.h9
-rw-r--r--src/inc/clrprivbinding.idl10
-rw-r--r--src/inc/loaderheap.h4
-rw-r--r--src/pal/prebuilt/inc/clrprivbinding.h8
-rw-r--r--src/utilcode/loaderheap.cpp7
-rw-r--r--src/vm/appdomain.cpp144
-rw-r--r--src/vm/appdomain.hpp11
-rw-r--r--src/vm/arm/cgencpu.h1
-rw-r--r--src/vm/arm/stubs.cpp5
-rw-r--r--src/vm/assembly.cpp54
-rw-r--r--src/vm/assemblynative.cpp57
-rw-r--r--src/vm/assemblynative.hpp3
-rw-r--r--src/vm/assemblyspec.cpp103
-rw-r--r--src/vm/assemblyspec.hpp6
-rw-r--r--src/vm/ceeload.cpp5
-rw-r--r--src/vm/clrprivbinderloadfile.h12
-rw-r--r--src/vm/clrprivbinderwinrt.h13
-rw-r--r--src/vm/comdelegate.cpp57
-rw-r--r--src/vm/comdelegate.h49
-rw-r--r--src/vm/coreassemblyspec.cpp2
-rw-r--r--src/vm/domainfile.cpp17
-rw-r--r--src/vm/domainfile.h21
-rw-r--r--src/vm/dynamicmethod.cpp2
-rw-r--r--src/vm/ecalllist.h1
-rw-r--r--src/vm/eventtrace.cpp29
-rw-r--r--src/vm/excep.cpp3
-rw-r--r--src/vm/hash.h8
-rw-r--r--src/vm/i386/stublinkerx86.cpp4
-rw-r--r--src/vm/i386/stublinkerx86.h1
-rw-r--r--src/vm/jithelpers.cpp6
-rw-r--r--src/vm/jitinterface.cpp7
-rw-r--r--src/vm/loaderallocator.cpp270
-rw-r--r--src/vm/loaderallocator.hpp93
-rw-r--r--src/vm/loaderallocator.inl28
-rw-r--r--src/vm/methodtable.inl2
-rw-r--r--src/vm/methodtablebuilder.cpp8
-rw-r--r--src/vm/stublink.cpp84
-rw-r--r--src/vm/stublink.h5
48 files changed, 1153 insertions, 399 deletions
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);