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

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Lapounov <antonl@microsoft.com>2017-06-14 04:12:55 +0300
committerAnton Lapounov <antonl@microsoft.com>2017-06-14 04:12:55 +0300
commit5fb55f6a1e2d1a2ae462ccaf50ade889728e41cb (patch)
tree1ac4628357cb765a80a93e95472ace145f849d58
parentd62fbb30583226ba56740836da98e52f1ac10278 (diff)
Introduce UnsafeGCHandle and enable the blittable check in GCHandle.set_Target.
CR: JKotas, FadiM, DavidWr At some moment the CallConverterThunk code took dependency on the blittable check missing in GCHandle.set_Target (change 1566458), so we had to place that check under "#if CORERT" (https://github.com/dotnet/corert/pull/1393). This change enables the check and introduces the UnsafeGCHandle structure to use in the CallConverterThunk code. UnsafeGCHandle is a faster version of the GCHandle structure with the following differences: * The constructor assumes the handle type is valid; no range check is performed. * The pinned flag is not stored in the _handle field. * The Target getter and setter assume the UnsafeGCHandle has been allocated. * No blittable check is performed when allocating a pinned UnsafeGCHandle or setting its target. * The GetRawTargetAddress method returns the raw address of the target (the pointer to its m_pEEType field). * The Free method is not thread-safe and does not throw if the UnsafeGCHandle has not been allocated or has been already freed. Also avoid the native runtime call in release builds in the RuntimeImports.RhHandleGet method and Runtime.Base's version of the UnsafeGCHandle.get_Target method (in debug builds the runtime performs additional checks). [tfs-changeset: 1661633]
-rw-r--r--src/Runtime.Base/src/System/Runtime/InteropServices/UnsafeGCHandle.cs7
-rw-r--r--src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs16
-rw-r--r--src/System.Private.CoreLib/src/MembersMustExist.AnalyzerData2
-rw-r--r--src/System.Private.CoreLib/src/System.Private.CoreLib.csproj6
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs4
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/InteropServices/UnsafeGCHandle.cs88
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs12
-rw-r--r--src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionParameters.cs35
-rw-r--r--src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs15
9 files changed, 133 insertions, 52 deletions
diff --git a/src/Runtime.Base/src/System/Runtime/InteropServices/UnsafeGCHandle.cs b/src/Runtime.Base/src/System/Runtime/InteropServices/UnsafeGCHandle.cs
index 31b7d2a26..b7b07f209 100644
--- a/src/Runtime.Base/src/System/Runtime/InteropServices/UnsafeGCHandle.cs
+++ b/src/Runtime.Base/src/System/Runtime/InteropServices/UnsafeGCHandle.cs
@@ -33,12 +33,17 @@ namespace System.Runtime.InteropServices
}
// Target property - allows getting / updating of the handle's referent.
- public Object Target
+ public unsafe Object Target
{
get
{
Debug.Assert(IsAllocated, "handle isn't initialized");
+#if DEBUG
+ // The runtime performs additional checks in debug builds
return InternalCalls.RhHandleGet(_handle);
+#else
+ return Unsafe.As<IntPtr, Object>(ref *(IntPtr*)_handle);
+#endif
}
set
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs
index cba64e9ae..ab23fccb0 100644
--- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs
@@ -926,22 +926,6 @@ namespace Internal.Runtime.Augments
return RuntimeImports.RhGetThunkSize();
}
- public static unsafe IntPtr GetRawAddrOfPinnedObject(IntPtr gcHandleAsIntPtr)
- {
- GCHandle gcHandle = (GCHandle)gcHandleAsIntPtr;
- Debug.Assert(gcHandle.IsPinned());
-
- Object target = gcHandle.Target;
-
- if (target == null)
- return IntPtr.Zero;
-
- fixed (IntPtr* pTargetEEType = &target.m_pEEType)
- {
- return (IntPtr)pTargetEEType;
- }
- }
-
[DebuggerStepThrough]
/* TEMP workaround due to bug 149078 */
[MethodImpl(MethodImplOptions.NoInlining)]
diff --git a/src/System.Private.CoreLib/src/MembersMustExist.AnalyzerData b/src/System.Private.CoreLib/src/MembersMustExist.AnalyzerData
index e321c569a..0d6dcb8da 100644
--- a/src/System.Private.CoreLib/src/MembersMustExist.AnalyzerData
+++ b/src/System.Private.CoreLib/src/MembersMustExist.AnalyzerData
@@ -125,7 +125,7 @@ internal static System.IntPtr System.Runtime.RuntimeImports.RhHandleAllocDepende
internal static System.IntPtr System.Runtime.RuntimeImports.RhHandleAllocVariable(object value, uint type)
internal static extern uint System.Runtime.RuntimeImports.RhHandleCompareExchangeVariableType(System.IntPtr handle, uint oldType, uint newType)
internal static extern void System.Runtime.RuntimeImports.RhHandleFree(System.IntPtr handle)
-internal static extern object System.Runtime.RuntimeImports.RhHandleGet(System.IntPtr handle)
+internal static object System.Runtime.RuntimeImports.RhHandleGet(System.IntPtr handle)
internal static extern object System.Runtime.RuntimeImports.RhHandleGetDependent(System.IntPtr handle, out object secondary)
internal static extern uint System.Runtime.RuntimeImports.RhHandleGetVariableType(System.IntPtr handle)
internal static extern void System.Runtime.RuntimeImports.RhHandleSet(System.IntPtr handle, object value)
diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj
index 1a1706b1e..7bb5fecda 100644
--- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj
+++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<PropertyGroup>
@@ -314,6 +314,7 @@
<Compile Include="System\Runtime\InteropServices\PInvokeMarshal.cs" />
<Compile Include="System\Runtime\InteropServices\SafeBuffer.cs" />
<Compile Include="System\Runtime\InteropServices\SafeHandle.cs" />
+ <Compile Include="System\Runtime\InteropServices\UnsafeGCHandle.cs" />
<Compile Include="System\Runtime\InteropServices\WindowsRuntime\EventRegistrationToken.cs" />
<Compile Include="System\Runtime\Loader\AssemblyLoadContext.cs" />
<Compile Include="System\SerializableAttribute.cs" />
@@ -834,9 +835,6 @@
<Compile Include="..\..\Runtime.Base\src\System\Runtime\CompilerServices\ManuallyManagedAttribute.cs">
<Link>Runtime.Base\src\System\Runtime\CompilerServices\ManuallyManagedAttribute.cs</Link>
</Compile>
- <Compile Include="..\..\Runtime.Base\src\System\Runtime\InteropServices\UnsafeGCHandle.cs">
- <Link>Runtime.Base\src\System\Runtime\InteropServices\UnsafeGCHandle.cs</Link>
- </Compile>
<Compile Include="..\..\Runtime.Base\src\RhBaseName.cs">
<Link>Runtime.Base\src\RhBaseName.cs</Link>
</Compile>
diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs
index 258a2f906..c31b24526 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs
@@ -3,8 +3,8 @@
// See the LICENSE file in the project root for more information.
using System;
-using System.Threading;
using System.Runtime.CompilerServices;
+using System.Threading;
namespace System.Runtime.InteropServices
{
@@ -114,10 +114,8 @@ namespace System.Runtime.InteropServices
throw new InvalidOperationException(); // SR.InvalidOperation_HandleIsNotInitialized);
}
-#if CORERT // TODO: Higher level ProjectN frameworks took dependency on this validation missing
if (IsPinned())
GCHandleValidatePinnedObject(value);
-#endif
RuntimeImports.RhHandleSet(GetHandleValue(), value);
}
diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/UnsafeGCHandle.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/UnsafeGCHandle.cs
new file mode 100644
index 000000000..953e606f7
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/UnsafeGCHandle.cs
@@ -0,0 +1,88 @@
+// 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.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Threading;
+
+namespace System.Runtime.InteropServices
+{
+ /// <summary>
+ /// The unsafe version of the GCHandle structure.
+ /// </summary>
+ /// <remarks>
+ /// Differences from the <c>GCHandle</c> structure:
+ /// <list type="bullet">
+ /// <item>The constructor assumes the handle type is valid; no range check is performed.</item>
+ /// <item>The pinned flag is not stored in the <c>_handle</c> field.</item>
+ /// <item>The <c>Target</c> getter and setter assume the <c>UnsafeGCHandle</c> has been allocated.</item>
+ /// <item>No blittable check is performed when allocating a pinned <c>UnsafeGCHandle</c> or setting its target.</item>
+ /// <item>The <c>GetRawTargetAddress</c> method returns the raw address of the target (the pointer to
+ /// its <c>m_pEEType</c> field).</item>
+ /// <item>The <c>Free</c> method is not thread-safe and does not throw if the <c>UnsafeGCHandle</c>
+ /// has not been allocated or has been already freed.</item>
+ /// </list>
+ /// </remarks>
+ [StructLayout(LayoutKind.Sequential)]
+ public struct UnsafeGCHandle
+ {
+ // IMPORTANT: This must be kept in sync with the GCHandleType enum.
+ private const GCHandleType MaxHandleType = GCHandleType.Pinned;
+
+ // The actual integer handle value that the EE uses internally.
+ private IntPtr _handle;
+
+ // Allocate a handle storing the object and the type.
+ private UnsafeGCHandle(Object value, GCHandleType type)
+ {
+ Debug.Assert((uint)type <= (uint)MaxHandleType, "Unexpected handle type");
+ _handle = RuntimeImports.RhHandleAlloc(value, type);
+ }
+
+ public static UnsafeGCHandle Alloc(Object value, GCHandleType type)
+ {
+ return new UnsafeGCHandle(value, type);
+ }
+
+ // Target property - allows getting / updating of the handle's referent.
+ public Object Target
+ {
+ get
+ {
+ Debug.Assert(IsAllocated, "Handle is not initialized");
+ return RuntimeImports.RhHandleGet(_handle);
+ }
+
+ set
+ {
+ Debug.Assert(IsAllocated, "Handle is not initialized");
+ RuntimeImports.RhHandleSet(_handle, value);
+ }
+ }
+
+ // Frees a GC handle. This method is not thread-safe!
+ public void Free()
+ {
+ if (_handle != default(IntPtr))
+ {
+ RuntimeImports.RhHandleFree(_handle);
+ }
+ }
+
+ // Returns the raw address of the target assuming it is pinned.
+ public unsafe IntPtr GetRawTargetAddress()
+ {
+ return *(IntPtr*)_handle;
+ }
+
+ // Determine whether this handle has been allocated or not.
+ private bool IsAllocated
+ {
+ get
+ {
+ return _handle != default(IntPtr);
+ }
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs
index 9516502bc..683dfa846 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs
@@ -234,7 +234,17 @@ namespace System.Runtime
// Get object reference from handle.
[MethodImpl(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhHandleGet")]
- internal static extern Object RhHandleGet(IntPtr handle);
+ private static extern Object _RhHandleGet(IntPtr handle);
+
+ internal static unsafe Object RhHandleGet(IntPtr handle)
+ {
+#if DEBUG
+ // The runtime performs additional checks in debug builds
+ return _RhHandleGet(handle);
+#else
+ return Unsafe.As<IntPtr, Object>(ref *(IntPtr*)handle);
+#endif
+ }
// Get primary and secondary object references from dependent handle.
[MethodImpl(MethodImplOptions.InternalCall)]
diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionParameters.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionParameters.cs
index 67e27009a..cdd3b4412 100644
--- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionParameters.cs
+++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionParameters.cs
@@ -63,17 +63,16 @@ namespace Internal.Runtime.TypeLoader
{
internal class GCHandleContainer
{
- internal GCHandle _thisPtrHandle;
- internal GCHandle _dynamicInvokeArgHandle;
- internal GCHandle _returnObjectHandle;
+ internal UnsafeGCHandle _thisPtrHandle;
+ internal UnsafeGCHandle _dynamicInvokeArgHandle;
+ internal UnsafeGCHandle _returnObjectHandle;
internal GCHandleContainer()
{
- // Allocations of pinned gc handles done only once during the lifetime of a thread.
- // An empty string is used as the initial pinned object reference.
- _thisPtrHandle = GCHandle.Alloc("", GCHandleType.Pinned);
- _dynamicInvokeArgHandle = GCHandle.Alloc("", GCHandleType.Pinned);
- _returnObjectHandle = GCHandle.Alloc("", GCHandleType.Pinned);
+ // Allocations of pinned gc handles done only once during the lifetime of a thread
+ _thisPtrHandle = UnsafeGCHandle.Alloc(null, GCHandleType.Pinned);
+ _dynamicInvokeArgHandle = UnsafeGCHandle.Alloc(null, GCHandleType.Pinned);
+ _returnObjectHandle = UnsafeGCHandle.Alloc(null, GCHandleType.Pinned);
}
~GCHandleContainer()
@@ -307,11 +306,11 @@ namespace Internal.Runtime.TypeLoader
internal void ResetPinnedObjects()
{
- // Reset all pinned gchandles to an empty string.
- // Freeing of gchandles is done in the destructor of GCHandleContainer when the thread dies
- s_pinnedGCHandles._thisPtrHandle.Target = "";
- s_pinnedGCHandles._returnObjectHandle.Target = "";
- s_pinnedGCHandles._dynamicInvokeArgHandle.Target = "";
+ // Reset all pinned gchandles to null.
+ // Freeing of gchandles is done in the destructor of GCHandleContainer when the thread dies.
+ s_pinnedGCHandles._thisPtrHandle.Target = null;
+ s_pinnedGCHandles._dynamicInvokeArgHandle.Target = null;
+ s_pinnedGCHandles._returnObjectHandle.Target = null;
}
private bool UpdateCalleeFunctionPointer(IntPtr newFunctionPointer)
@@ -417,7 +416,7 @@ namespace Internal.Runtime.TypeLoader
Debug.Assert(_conversionInfo.IsClosedStaticDelegate && !_delegateData.Equals(default(DelegateData)));
Debug.Assert(_delegateData._helperObject != null);
s_pinnedGCHandles._thisPtrHandle.Target = _delegateData._helperObject;
- return RuntimeAugments.GetRawAddrOfPinnedObject((IntPtr)s_pinnedGCHandles._thisPtrHandle);
+ return s_pinnedGCHandles._thisPtrHandle.GetRawTargetAddress();
}
}
@@ -472,7 +471,7 @@ namespace Internal.Runtime.TypeLoader
s_pinnedGCHandles._thisPtrHandle.Target = _delegateData._helperObject;
}
- thisPointer = (void*)RuntimeAugments.GetRawAddrOfPinnedObject((IntPtr)s_pinnedGCHandles._thisPtrHandle);
+ thisPointer = (void*)s_pinnedGCHandles._thisPtrHandle.GetRawTargetAddress();
}
else
{
@@ -524,10 +523,10 @@ namespace Internal.Runtime.TypeLoader
// The transition block has a space reserved for storing return buffer data. This is protected conservatively.
// Copy the address of the allocated object to the protected memory to be able to safely unpin it.
callerRetBuffer = _callerTransitionBlock + TransitionBlock.GetOffsetOfReturnValuesBlock();
- *((void**)callerRetBuffer) = (void*)RuntimeAugments.GetRawAddrOfPinnedObject((IntPtr)s_pinnedGCHandles._returnObjectHandle);
+ *((void**)callerRetBuffer) = (void*)s_pinnedGCHandles._returnObjectHandle.GetRawTargetAddress();
// Unpin the allocated object (it's now protected in the caller's conservatively reported memory space)
- s_pinnedGCHandles._returnObjectHandle.Target = "";
+ s_pinnedGCHandles._returnObjectHandle.Target = null;
// Point the callerRetBuffer to the begining of the actual object's data (skipping the EETypePtr slot)
callerRetBuffer = (void*)(new IntPtr(*((void**)callerRetBuffer)) + IntPtr.Size);
@@ -632,7 +631,7 @@ namespace Internal.Runtime.TypeLoader
s_pinnedGCHandles._returnObjectHandle.Target = targetDelegate(arguments ?? Array.Empty<object>());
- return RuntimeAugments.GetRawAddrOfPinnedObject((IntPtr)s_pinnedGCHandles._returnObjectHandle);
+ return s_pinnedGCHandles._returnObjectHandle.GetRawTargetAddress();
}
internal IntPtr GetArgSetupStateDataPointer()
diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs
index 73b169b74..5c2e25582 100644
--- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs
+++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs
@@ -684,7 +684,7 @@ namespace Internal.Runtime.TypeLoader
if (paramLookupType == InvokeUtils.DynamicInvokeParamLookupType.ValuetypeObjectReturned)
{
CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.Target = invokeParam;
- argPtr = RuntimeAugments.GetRawAddrOfPinnedObject((IntPtr)CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle) + IntPtr.Size;
+ argPtr = CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.GetRawTargetAddress() + IntPtr.Size;
}
else
{
@@ -692,7 +692,7 @@ namespace Internal.Runtime.TypeLoader
Debug.Assert((invokeParam is object[]) && index < ((object[])invokeParam).Length);
CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.Target = ((object[])invokeParam)[index];
- pinnedResultObject = RuntimeAugments.GetRawAddrOfPinnedObject((IntPtr)CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle);
+ pinnedResultObject = CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.GetRawTargetAddress();
if (conversionParams._calleeArgs.IsArgPassedByRef())
{
@@ -709,16 +709,15 @@ namespace Internal.Runtime.TypeLoader
{
// Input byref parameter has a null value
conversionParams._dynamicInvokeByRefObjectArgs[index] = new DynamicInvokeByRefArgObjectWrapper();
- CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.Target = conversionParams._dynamicInvokeByRefObjectArgs[index];
- argPtr = RuntimeAugments.GetRawAddrOfPinnedObject((IntPtr)CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle) + IntPtr.Size;
}
else
{
// Input byref parameter has a non-null value
conversionParams._dynamicInvokeByRefObjectArgs[index] = new DynamicInvokeByRefArgObjectWrapper { _object = conversionParams._dynamicInvokeParams[index] };
- CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.Target = conversionParams._dynamicInvokeByRefObjectArgs[index];
- argPtr = RuntimeAugments.GetRawAddrOfPinnedObject((IntPtr)CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle) + IntPtr.Size;
}
+
+ CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.Target = conversionParams._dynamicInvokeByRefObjectArgs[index];
+ argPtr = CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.GetRawTargetAddress() + IntPtr.Size;
}
else
{
@@ -843,7 +842,7 @@ namespace Internal.Runtime.TypeLoader
{
// The calleeTransitionBlock is GC-protected, so we can now safely unpin the return value of DynamicInvokeParamHelperCore,
// since we just copied it to the callee TB.
- CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.Target = "";
+ CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.Target = null;
}
arg++;
@@ -1111,7 +1110,7 @@ namespace Internal.Runtime.TypeLoader
// Need to box value type before returning it
object returnValue = RuntimeAugments.Box(thValueType.GetRuntimeTypeHandle(), new IntPtr(returnValueToCopy));
CallConversionParameters.s_pinnedGCHandles._returnObjectHandle.Target = returnValue;
- pinnedResultObject = RuntimeAugments.GetRawAddrOfPinnedObject((IntPtr)CallConversionParameters.s_pinnedGCHandles._returnObjectHandle);
+ pinnedResultObject = CallConversionParameters.s_pinnedGCHandles._returnObjectHandle.GetRawTargetAddress();
returnValueToCopy = (void*)&pinnedResultObject;
#if _TARGET_X86_