Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 5e1ef69

Browse files
authored
Change BulkMoveWithWriteBarrier to be GC suspension friendly (#27642)
* Change BulkMoveWithWriteBarrier to be GC suspension friendly * Rename RuntimeHelpers.cs
1 parent 5216c8c commit 5e1ef69

File tree

7 files changed

+108
-34
lines changed

7 files changed

+108
-34
lines changed

src/System.Private.CoreLib/System.Private.CoreLib.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,8 @@
141141
<!-- Sources -->
142142
<ItemGroup>
143143
<Compile Include="$(BclSourcesRoot)\Internal\Console.cs" />
144-
<Compile Include="$(BclSourcesRoot)\Internal\Runtime\InteropServices\IsolatedComponentLoadContext.cs" />
145144
<Compile Include="$(BclSourcesRoot)\Internal\Runtime\InteropServices\ComponentActivator.cs" />
145+
<Compile Include="$(BclSourcesRoot)\Internal\Runtime\InteropServices\IsolatedComponentLoadContext.cs" />
146146
<Compile Include="$(BclSourcesRoot)\Microsoft\Win32\UnsafeNativeMethods.cs" />
147147
<Compile Include="$(BclSourcesRoot)\System\__Canon.cs" />
148148
<Compile Include="$(BclSourcesRoot)\System\ArgIterator.cs" />
@@ -161,13 +161,13 @@
161161
<Compile Include="$(BclSourcesRoot)\System\Delegate.CoreCLR.cs" />
162162
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Debugger.cs" />
163163
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\EditAndContinueHelper.cs" />
164-
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\NativeRuntimeEventSource.cs" Condition="'$(FeaturePerfTracing)' == 'true'" />
165164
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipe.cs" />
166165
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipeController.cs" />
167166
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipeEventDispatcher.cs" />
168167
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipeEventProvider.cs" />
169168
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipeMetadataGenerator.cs" />
170169
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipePayloadDecoder.cs" />
170+
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\NativeRuntimeEventSource.cs" Condition="'$(FeaturePerfTracing)' == 'true'" />
171171
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\RuntimeEventSource.cs" Condition="'$(FeaturePerfTracing)' == 'true'" />
172172
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\TraceLogging\TraceLoggingEventHandleTable.cs" />
173173
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\ICustomDebuggerNotification.cs" />
@@ -246,14 +246,13 @@
246246
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeParameterInfo.cs" />
247247
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimePropertyInfo.cs" />
248248
<Compile Include="$(BclSourcesRoot)\System\Resources\ManifestBasedResourceGroveler.CoreCLR.cs" />
249-
<Compile Include="$(BclSourcesRoot)\System\RuntimeType.CoreCLR.cs" />
250249
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\CrossLoaderAllocatorHashHelpers.cs" />
251250
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\DependentHandle.cs" />
252251
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\GCHeapHash.cs" />
253252
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\ICastableHelpers.cs" />
254253
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\QCallHandles.cs" />
255254
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeFeature.CoreCLR.cs" />
256-
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeHelpers.cs" />
255+
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeHelpers.CoreCLR.cs" />
257256
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\TypeDependencyAttribute.cs" />
258257
<Compile Include="$(BclSourcesRoot)\System\Runtime\GCSettings.CoreCLR.cs" />
259258
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ComTypes\IEnumerable.cs" />
@@ -266,6 +265,7 @@
266265
<Compile Include="$(BclSourcesRoot)\System\Runtime\Versioning\CompatibilitySwitch.cs" />
267266
<Compile Include="$(BclSourcesRoot)\System\RuntimeArgumentHandle.cs" />
268267
<Compile Include="$(BclSourcesRoot)\System\RuntimeHandles.cs" />
268+
<Compile Include="$(BclSourcesRoot)\System\RuntimeType.CoreCLR.cs" />
269269
<Compile Include="$(BclSourcesRoot)\System\Security\DynamicSecurityMethodAttribute.cs" />
270270
<Compile Include="$(BclSourcesRoot)\System\StartupHookProvider.cs" />
271271
<Compile Include="$(BclSourcesRoot)\System\String.CoreCLR.cs" />
@@ -290,8 +290,8 @@
290290
<Compile Include="$(BclSourcesRoot)\System\ValueType.cs" />
291291
<Compile Include="$(BclSourcesRoot)\System\WeakReference.CoreCLR.cs" />
292292
<Compile Include="$(BclSourcesRoot)\System\WeakReference.T.CoreCLR.cs" />
293-
<Compile Include="shared\Interop\Windows\Kernel32\Interop.HandleTypes.cs" />
294293
<Compile Include="shared\Interop\Windows\Kernel32\Interop.GetStdHandle.cs" />
294+
<Compile Include="shared\Interop\Windows\Kernel32\Interop.HandleTypes.cs" />
295295
<Compile Include="shared\Interop\Windows\Kernel32\Interop.LocalAlloc.cs" />
296296
<Compile Include="shared\Interop\Windows\Ole32\Interop.CoTaskMemAlloc.cs" />
297297
<Compile Include="shared\Interop\Windows\OleAut32\Interop.SysAllocStringByteLen.cs" />

src/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
#pragma warning disable SA1121 // explicitly using type aliases instead of built-in types
1111
#if BIT64
1212
using nuint = System.UInt64;
13+
using nint = System.UInt64;
1314
#else
1415
using nuint = System.UInt32;
16+
using nint = System.UInt32;
1517
#endif
1618

1719
namespace System
@@ -37,8 +39,53 @@ internal static unsafe void _ZeroMemory(ref byte b, nuint byteLength)
3739
[DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
3840
private static extern unsafe void __ZeroMemory(void* b, nuint byteLength);
3941

42+
// The maximum block size to for __BulkMoveWithWriteBarrier FCall. This is required to avoid GC starvation.
43+
private const uint BulkMoveWithWriteBarrierChunk = 0x4000;
44+
45+
internal static void BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount)
46+
{
47+
if (byteCount <= BulkMoveWithWriteBarrierChunk)
48+
__BulkMoveWithWriteBarrier(ref destination, ref source, byteCount);
49+
else
50+
_BulkMoveWithWriteBarrier(ref destination, ref source, byteCount);
51+
}
52+
53+
// Non-inlinable wrapper around the loop for copying large blocks in chunks
54+
[MethodImpl(MethodImplOptions.NoInlining)]
55+
private static void _BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount)
56+
{
57+
Debug.Assert(byteCount > BulkMoveWithWriteBarrierChunk);
58+
59+
if (Unsafe.AreSame(ref destination, ref source))
60+
return;
61+
62+
if ((nuint)(nint)Unsafe.ByteOffset(ref destination, ref source) >= byteCount)
63+
{
64+
// Copy forwards
65+
do
66+
{
67+
byteCount -= BulkMoveWithWriteBarrierChunk;
68+
__BulkMoveWithWriteBarrier(ref destination, ref source, BulkMoveWithWriteBarrierChunk);
69+
destination = ref Unsafe.AddByteOffset(ref destination, BulkMoveWithWriteBarrierChunk);
70+
source = ref Unsafe.AddByteOffset(ref source, BulkMoveWithWriteBarrierChunk);
71+
}
72+
while (byteCount > BulkMoveWithWriteBarrierChunk);
73+
}
74+
else
75+
{
76+
// Copy backwards
77+
do
78+
{
79+
byteCount -= BulkMoveWithWriteBarrierChunk;
80+
__BulkMoveWithWriteBarrier(ref Unsafe.AddByteOffset(ref destination, byteCount), ref Unsafe.AddByteOffset(ref source, byteCount), BulkMoveWithWriteBarrierChunk);
81+
}
82+
while (byteCount > BulkMoveWithWriteBarrierChunk);
83+
}
84+
__BulkMoveWithWriteBarrier(ref destination, ref source, byteCount);
85+
}
86+
4087
[MethodImpl(MethodImplOptions.InternalCall)]
41-
internal static extern void BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount);
88+
private static extern void __BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount);
4289

4390
[DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
4491
private static extern unsafe void __Memmove(byte* dest, byte* src, nuint len);

src/System.Private.CoreLib/src/System/Object.CoreCLR.cs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@
44

55
using System.Runtime.CompilerServices;
66

7+
using Internal.Runtime.CompilerServices;
8+
9+
#pragma warning disable SA1121 // explicitly using type aliases instead of built-in types
10+
#if BIT64
11+
using nuint = System.UInt64;
12+
#else
13+
using nuint = System.UInt32;
14+
#endif
15+
716
namespace System
817
{
918
public partial class Object
@@ -16,7 +25,22 @@ public partial class Object
1625
// object. This is always a shallow copy of the instance. The method is protected
1726
// so that other object may only call this method on themselves. It is intended to
1827
// support the ICloneable interface.
19-
[MethodImpl(MethodImplOptions.InternalCall)]
20-
protected extern object MemberwiseClone();
28+
protected unsafe object MemberwiseClone()
29+
{
30+
object clone = RuntimeHelpers.AllocateUninitializedClone(this);
31+
32+
// copy contents of "this" to the clone
33+
34+
nuint byteCount = RuntimeHelpers.GetRawObjectDataSize(clone);
35+
ref byte src = ref this.GetRawData();
36+
ref byte dst = ref clone.GetRawData();
37+
38+
if (RuntimeHelpers.GetMethodTable(clone)->ContainsGCPointers)
39+
Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount);
40+
else
41+
Buffer.Memmove(ref dst, ref src, byteCount);
42+
43+
return clone;
44+
}
2145
}
2246
}

src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs renamed to src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ public static int OffsetToStringData
149149
[MethodImpl(MethodImplOptions.InternalCall)]
150150
private static extern object GetUninitializedObjectInternal(Type type);
151151

152+
[MethodImpl(MethodImplOptions.InternalCall)]
153+
internal static extern object AllocateUninitializedClone(object obj);
154+
152155
/// <returns>true if given type is reference type or value type that contains references</returns>
153156
[Intrinsic]
154157
public static bool IsReferenceOrContainsReferences<T>()
@@ -189,6 +192,21 @@ internal static int EnumCompareTo<T>(T x, T y) where T : struct, Enum
189192
internal static ref byte GetRawData(this object obj) =>
190193
ref Unsafe.As<RawData>(obj).Data;
191194

195+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
196+
internal static unsafe nuint GetRawObjectDataSize(object obj)
197+
{
198+
MethodTable* pMT = GetMethodTable(obj);
199+
200+
// See comment on RawArrayData for details
201+
nuint rawSize = pMT->BaseSize - (nuint)(2 * sizeof(IntPtr));
202+
if (pMT->HasComponentSize)
203+
rawSize += (uint)Unsafe.As<RawArrayData>(obj).Length * (nuint)pMT->ComponentSize;
204+
205+
GC.KeepAlive(obj); // Keep MethodTable alive
206+
207+
return rawSize;
208+
}
209+
192210
[Intrinsic]
193211
internal static ref byte GetRawSzArrayData(this Array array) =>
194212
ref Unsafe.As<RawArrayData>(array).Data;

src/classlibnative/bcltype/objectnative.cpp

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -221,22 +221,19 @@ FCIMPL1(Object*, ObjectNative::GetClass, Object* pThis)
221221
}
222222
FCIMPLEND
223223

224-
FCIMPL1(Object*, ObjectNative::Clone, Object* pThisUNSAFE)
224+
FCIMPL1(Object*, ObjectNative::AllocateUninitializedClone, Object* pObjUNSAFE)
225225
{
226226
FCALL_CONTRACT;
227227

228-
OBJECTREF refClone = NULL;
229-
OBJECTREF refThis = ObjectToOBJECTREF(pThisUNSAFE);
228+
// Delegate error handling to managed side (it will throw NullRefenceException)
229+
if (pObjUNSAFE == NULL)
230+
return NULL;
230231

231-
if (refThis == NULL)
232-
FCThrow(kNullReferenceException);
233-
234-
HELPER_METHOD_FRAME_BEGIN_RET_2(refClone, refThis);
232+
OBJECTREF refClone = ObjectToOBJECTREF(pObjUNSAFE);
235233

236-
// ObjectNative::Clone() ensures that the source and destination are always in
237-
// the same context.
234+
HELPER_METHOD_FRAME_BEGIN_RET_1(refClone);
238235

239-
MethodTable* pMT = refThis->GetMethodTable();
236+
MethodTable* pMT = refClone->GetMethodTable();
240237

241238
// assert that String has overloaded the Clone() method
242239
_ASSERTE(pMT != g_pStringClass);
@@ -245,25 +242,13 @@ FCIMPL1(Object*, ObjectNative::Clone, Object* pThisUNSAFE)
245242
#endif // FEATURE_UTF8STRING
246243

247244
if (pMT->IsArray()) {
248-
refClone = DupArrayForCloning((BASEARRAYREF)refThis);
245+
refClone = DupArrayForCloning((BASEARRAYREF)refClone);
249246
} else {
250247
// We don't need to call the <cinit> because we know
251248
// that it has been called....(It was called before this was created)
252249
refClone = AllocateObject(pMT);
253250
}
254251

255-
SIZE_T cb = refThis->GetSize() - sizeof(ObjHeader);
256-
257-
// copy contents of "this" to the clone
258-
if (pMT->ContainsPointers())
259-
{
260-
memmoveGCRefs(OBJECTREFToObject(refClone), OBJECTREFToObject(refThis), cb);
261-
}
262-
else
263-
{
264-
memcpyNoGCRefs(OBJECTREFToObject(refClone), OBJECTREFToObject(refThis), cb);
265-
}
266-
267252
HELPER_METHOD_FRAME_END();
268253

269254
return OBJECTREFToObject(refClone);

src/classlibnative/bcltype/objectnative.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class ObjectNative
3232
static FCDECL1(Object*, GetObjectValue, Object* vThisRef);
3333
static FCDECL1(INT32, GetHashCode, Object* vThisRef);
3434
static FCDECL2(FC_BOOL_RET, Equals, Object *pThisRef, Object *pCompareRef);
35-
static FCDECL1(Object*, Clone, Object* pThis);
35+
static FCDECL1(Object*, AllocateUninitializedClone, Object* pObjUNSAFE);
3636
static FCDECL1(Object*, GetClass, Object* pThis);
3737
static FCDECL3(FC_BOOL_RET, WaitTimeout, CLR_BOOL exitContext, INT32 Timeout, Object* pThisUNSAFE);
3838
static FCDECL1(void, Pulse, Object* pThisUNSAFE);

src/vm/ecalllist.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ FCFuncEnd()
9292

9393
FCFuncStart(gObjectFuncs)
9494
FCIntrinsic("GetType", ObjectNative::GetClass, CORINFO_INTRINSIC_Object_GetType)
95-
FCFuncElement("MemberwiseClone", ObjectNative::Clone)
9695
FCFuncEnd()
9796

9897
FCFuncStart(gStringFuncs)
@@ -729,7 +728,7 @@ FCFuncEnd()
729728
FCFuncStart(gBufferFuncs)
730729
FCFuncElement("IsPrimitiveTypeArray", Buffer::IsPrimitiveTypeArray)
731730
QCFuncElement("__ZeroMemory", Buffer::Clear)
732-
FCFuncElement("BulkMoveWithWriteBarrier", Buffer::BulkMoveWithWriteBarrier)
731+
FCFuncElement("__BulkMoveWithWriteBarrier", Buffer::BulkMoveWithWriteBarrier)
733732
QCFuncElement("__Memmove", Buffer::MemMove)
734733
FCFuncEnd()
735734

@@ -913,6 +912,7 @@ FCFuncStart(gRuntimeHelpers)
913912
FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate)
914913
FCFuncElement("GetHashCode", ObjectNative::GetHashCode)
915914
FCFuncElement("Equals", ObjectNative::Equals)
915+
FCFuncElement("AllocateUninitializedClone", ObjectNative::AllocateUninitializedClone)
916916
FCFuncElement("EnsureSufficientExecutionStack", ReflectionInvocation::EnsureSufficientExecutionStack)
917917
FCFuncElement("TryEnsureSufficientExecutionStack", ReflectionInvocation::TryEnsureSufficientExecutionStack)
918918
FCFuncElement("GetUninitializedObjectInternal", ReflectionSerialization::GetUninitializedObject)

0 commit comments

Comments
 (0)