Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarek Safar <marek.safar@gmail.com>2019-02-23 13:06:32 +0300
committerMarek Safar <marek.safar@gmail.com>2019-02-26 01:39:31 +0300
commit5ae98353a6f50de71b8db70566fe190e4174f9ef (patch)
treed74f20bcd981ace62d8a66487eca2f04d1c21c42 /netcore/System.Private.CoreLib/shared/System/Runtime/Loader/AssemblyLoadContext.cs
parent7f3a127b6d335f6f50d629a7904c6aa9386e0afb (diff)
Move AssemblyLoadContext to shared partition (dotnet/coreclr#22685)
* Move AssemblyLoadContext to shared partition * Move static initializer to DefaultAssemblyLoadContext and remove stream copying from lock scope Signed-off-by: dotnet-bot <dotnet-bot@microsoft.com>
Diffstat (limited to 'netcore/System.Private.CoreLib/shared/System/Runtime/Loader/AssemblyLoadContext.cs')
-rw-r--r--netcore/System.Private.CoreLib/shared/System/Runtime/Loader/AssemblyLoadContext.cs370
1 files changed, 370 insertions, 0 deletions
diff --git a/netcore/System.Private.CoreLib/shared/System/Runtime/Loader/AssemblyLoadContext.cs b/netcore/System.Private.CoreLib/shared/System/Runtime/Loader/AssemblyLoadContext.cs
new file mode 100644
index 00000000000..f13bf5ce9cf
--- /dev/null
+++ b/netcore/System.Private.CoreLib/shared/System/Runtime/Loader/AssemblyLoadContext.cs
@@ -0,0 +1,370 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace System.Runtime.Loader
+{
+ public abstract partial class AssemblyLoadContext
+ {
+ 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
+ }
+
+ private static readonly Dictionary<long, WeakReference<AssemblyLoadContext>> s_contextsToUnload = new Dictionary<long, WeakReference<AssemblyLoadContext>>();
+ private static long s_nextId;
+ private static bool s_isProcessExiting;
+
+ // Indicates the state of this ALC (Alive or in Unloading state)
+ private InternalState _state;
+
+ // Id used by s_contextsToUnload
+ private readonly long _id;
+
+ // synchronization primitive to protect against usage of this instance while unloading
+ private readonly object _unloadLock;
+
+ // Contains the reference to VM's representation of the AssemblyLoadContext
+ private readonly IntPtr _nativeAssemblyLoadContext;
+
+ protected AssemblyLoadContext() : this(false, false)
+ {
+ }
+
+ protected AssemblyLoadContext(bool isCollectible) : this(false, isCollectible)
+ {
+ }
+
+ private protected AssemblyLoadContext(bool representsTPALoadContext, bool isCollectible)
+ {
+ // Initialize the VM side of AssemblyLoadContext if not already done.
+ IsCollectible = isCollectible;
+ // The _unloadLock needs to be assigned after the IsCollectible to ensure proper behavior of the finalizer
+ // even in case the following allocation fails or the thread is aborted between these two lines.
+ _unloadLock = new object();
+
+ if (!isCollectible)
+ {
+ // For non collectible AssemblyLoadContext, the finalizer should never be called and thus the AssemblyLoadContext should not
+ // be on the finalizer queue.
+ GC.SuppressFinalize(this);
+ }
+
+ // Add this instance to the list of alive ALC
+ lock (s_contextsToUnload)
+ {
+ if (s_isProcessExiting)
+ {
+ throw new InvalidOperationException(SR.AssemblyLoadContext_Constructor_CannotInstantiateWhileUnloading);
+ }
+
+ // If this is a collectible ALC, we are creating a weak handle tracking resurrection otherwise we use a strong handle
+ var thisHandle = GCHandle.Alloc(this, IsCollectible ? GCHandleType.WeakTrackResurrection : GCHandleType.Normal);
+ var thisHandlePtr = GCHandle.ToIntPtr(thisHandle);
+ _nativeAssemblyLoadContext = InitializeAssemblyLoadContext(thisHandlePtr, representsTPALoadContext, isCollectible);
+
+ _id = s_nextId++;
+ s_contextsToUnload.Add(_id, new WeakReference<AssemblyLoadContext>(this, true));
+ }
+ }
+
+ ~AssemblyLoadContext()
+ {
+ // Use the _unloadLock as a guard to detect the corner case when the constructor of the AssemblyLoadContext was not executed
+ // e.g. due to the JIT failing to JIT it.
+ if (_unloadLock != null)
+ {
+ // Only valid for a Collectible ALC. Non-collectible ALCs have the finalizer suppressed.
+ Debug.Assert(IsCollectible);
+ // We get here only in case the explicit Unload was not initiated.
+ Debug.Assert(_state != InternalState.Unloading);
+ InitiateUnload();
+ }
+ }
+
+ private void InitiateUnload()
+ {
+ var unloading = Unloading;
+ Unloading = null;
+ unloading?.Invoke(this);
+
+ // 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 (!s_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(_nativeAssemblyLoadContext, thisStrongHandlePtr);
+ }
+
+ _state = InternalState.Unloading;
+ }
+
+ if (!s_isProcessExiting)
+ {
+ lock (s_contextsToUnload)
+ {
+ s_contextsToUnload.Remove(_id);
+ }
+ }
+ }
+
+ // Event handler for resolving native libraries.
+ // This event is raised if the native library could not be resolved via
+ // the default resolution logic [including AssemblyLoadContext.LoadUnmanagedDll()]
+ //
+ // Inputs: Invoking assembly, and library name to resolve
+ // Returns: A handle to the loaded native library
+ public event Func<Assembly, string, IntPtr> ResolvingUnmanagedDll;
+
+ // Event handler for resolving managed assemblies.
+ // This event is raised if the managed assembly could not be resolved via
+ // the default resolution logic [including AssemblyLoadContext.Load()]
+ //
+ // Inputs: The AssemblyLoadContext and AssemblyName to be loaded
+ // Returns: The Loaded assembly object.
+ public event Func<AssemblyLoadContext, AssemblyName, Assembly> Resolving;
+
+ public event Action<AssemblyLoadContext> Unloading;
+
+ // Occurs when an Assembly is loaded
+ public static event AssemblyLoadEventHandler AssemblyLoad;
+
+ // Occurs when resolution of type fails
+ public static event ResolveEventHandler TypeResolve;
+
+ // Occurs when resolution of resource fails
+ public static event ResolveEventHandler ResourceResolve;
+
+ // Occurs when resolution of assembly fails
+ // This event is fired after resolve events of AssemblyLoadContext fails
+ public static event ResolveEventHandler AssemblyResolve;
+
+ public static AssemblyLoadContext Default => DefaultAssemblyLoadContext.s_loadContext;
+
+ public bool IsCollectible { get; }
+
+ // Helper to return AssemblyName corresponding to the path of an IL assembly
+ public static AssemblyName GetAssemblyName(string assemblyPath)
+ {
+ if (assemblyPath == null)
+ {
+ throw new ArgumentNullException(nameof(assemblyPath));
+ }
+
+ return AssemblyName.GetAssemblyName(assemblyPath);
+ }
+
+ // Custom AssemblyLoadContext implementations can override this
+ // method to perform custom processing and use one of the protected
+ // helpers above to load the assembly.
+ protected abstract Assembly Load(AssemblyName assemblyName);
+
+ [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
+ public Assembly LoadFromAssemblyName(AssemblyName assemblyName)
+ {
+ if (assemblyName == null)
+ throw new ArgumentNullException(nameof(assemblyName));
+
+ // Attempt to load the assembly, using the same ordering as static load, in the current load context.
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return Assembly.Load(assemblyName, ref stackMark, _nativeAssemblyLoadContext);
+ }
+
+ // These methods load assemblies into the current AssemblyLoadContext
+ // They may be used in the implementation of an AssemblyLoadContext derivation
+ public Assembly LoadFromAssemblyPath(string assemblyPath)
+ {
+ if (assemblyPath == null)
+ {
+ throw new ArgumentNullException(nameof(assemblyPath));
+ }
+
+ if (PathInternal.IsPartiallyQualified(assemblyPath))
+ {
+ throw new ArgumentException(SR.Argument_AbsolutePathRequired, nameof(assemblyPath));
+ }
+
+ lock (_unloadLock)
+ {
+ VerifyIsAlive();
+
+ return InternalLoadFromPath(assemblyPath, null);
+ }
+ }
+
+ public Assembly LoadFromNativeImagePath(string nativeImagePath, string assemblyPath)
+ {
+ if (nativeImagePath == null)
+ {
+ throw new ArgumentNullException(nameof(nativeImagePath));
+ }
+
+ if (PathInternal.IsPartiallyQualified(nativeImagePath))
+ {
+ throw new ArgumentException(SR.Argument_AbsolutePathRequired, nameof(nativeImagePath));
+ }
+
+ if (assemblyPath != null && PathInternal.IsPartiallyQualified(assemblyPath))
+ {
+ throw new ArgumentException(SR.Argument_AbsolutePathRequired, nameof(assemblyPath));
+ }
+
+ lock (_unloadLock)
+ {
+ VerifyIsAlive();
+
+ return InternalLoadFromPath(assemblyPath, nativeImagePath);
+ }
+ }
+
+ public Assembly LoadFromStream(Stream assembly)
+ {
+ return LoadFromStream(assembly, null);
+ }
+
+ public Assembly LoadFromStream(Stream assembly, Stream assemblySymbols)
+ {
+ if (assembly == null)
+ {
+ throw new ArgumentNullException(nameof(assembly));
+ }
+
+ int iAssemblyStreamLength = (int)assembly.Length;
+
+ if (iAssemblyStreamLength <= 0)
+ {
+ throw new BadImageFormatException(SR.BadImageFormat_BadILFormat);
+ }
+
+ // Allocate the byte[] to hold the assembly
+ byte[] arrAssembly = new byte[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)
+ {
+ var iSymbolLength = (int)assemblySymbols.Length;
+ arrSymbols = new byte[iSymbolLength];
+
+ assemblySymbols.Read(arrSymbols, 0, iSymbolLength);
+ }
+
+ lock (_unloadLock)
+ {
+ VerifyIsAlive();
+
+ return InternalLoadFromStream(arrAssembly, arrSymbols);
+ }
+ }
+
+ // This method provides a way for overriders of LoadUnmanagedDll() to load an unmanaged DLL from a specific path in a
+ // platform-independent way. The DLL is loaded with default load flags.
+ protected IntPtr LoadUnmanagedDllFromPath(string unmanagedDllPath)
+ {
+ if (unmanagedDllPath == null)
+ {
+ throw new ArgumentNullException(nameof(unmanagedDllPath));
+ }
+
+ if (unmanagedDllPath.Length == 0)
+ {
+ throw new ArgumentException(SR.Argument_EmptyPath, nameof(unmanagedDllPath));
+ }
+
+ if (PathInternal.IsPartiallyQualified(unmanagedDllPath))
+ {
+ throw new ArgumentException(SR.Argument_AbsolutePathRequired, nameof(unmanagedDllPath));
+ }
+
+ return InternalLoadUnmanagedDllFromPath(unmanagedDllPath);
+ }
+
+ // Custom AssemblyLoadContext implementations can override this
+ // method to perform the load of unmanaged native dll
+ // This function needs to return the HMODULE of the dll it loads
+ protected virtual IntPtr LoadUnmanagedDll(string unmanagedDllName)
+ {
+ //defer to default coreclr policy of loading unmanaged dll
+ return IntPtr.Zero;
+ }
+
+ public void Unload()
+ {
+ if (!IsCollectible)
+ {
+ throw new InvalidOperationException(SR.AssemblyLoadContext_Unload_CannotUnloadIfNotCollectible);
+ }
+
+ GC.SuppressFinalize(this);
+ InitiateUnload();
+ }
+
+ internal static void OnProcessExit()
+ {
+ lock (s_contextsToUnload)
+ {
+ s_isProcessExiting = true;
+ foreach (var alcAlive in s_contextsToUnload)
+ {
+ if (alcAlive.Value.TryGetTarget(out AssemblyLoadContext alc))
+ {
+ // Should we use a try/catch?
+ alc.InitiateUnload();
+ }
+ }
+ s_contextsToUnload.Clear();
+ }
+ }
+
+ private void VerifyIsAlive()
+ {
+ if (_state != InternalState.Alive)
+ {
+ throw new InvalidOperationException(SR.AssemblyLoadContext_Verify_NotUnloading);
+ }
+ }
+ }
+
+ internal sealed class DefaultAssemblyLoadContext : AssemblyLoadContext
+ {
+ internal static readonly AssemblyLoadContext s_loadContext = new DefaultAssemblyLoadContext();
+
+ internal DefaultAssemblyLoadContext() : base(true, false)
+ {
+ }
+
+ protected override Assembly Load(AssemblyName assemblyName)
+ {
+ // We were loading an assembly into TPA ALC that was not found on TPA list. As a result we are here.
+ // Returning null will result in the AssemblyResolve event subscribers to be invoked to help resolve the assembly.
+ return null;
+ }
+ }
+} \ No newline at end of file