From c0ddd1c5d1636de873398c8d9544e02289f95fec Mon Sep 17 00:00:00 2001 From: Vladimir Sadov Date: Tue, 17 Mar 2020 22:00:41 -0700 Subject: [PATCH] Adding public API for Pinned Object Heap allocations (#33526) * Adding API for POH allocations and propagating flags all the way to Alloc. * make `AllocateUninitializedArray` and `AllocateArray` public * Added NYI implementations to Mono * moved tests to libraries * Actually use POH and more tests. * Disable tests for the new API on mono * mop up remaining TODOs * Fix build breaking whitespace. * Mono tabs and mark heavier tests as [Outerloop] * Mono space before openning parens and braces * Refactored AllocateArray * PR feedback * XML Doc comments --- .../src/System.Private.CoreLib/ILLinkTrim.xml | 2 - .../src/System.Private.CoreLib/src/System/GC.cs | 81 ++++- src/coreclr/src/gc/gc.cpp | 9 +- src/coreclr/src/gc/gcinterface.h | 3 +- src/coreclr/src/vm/comutilnative.cpp | 7 +- src/coreclr/src/vm/comutilnative.h | 2 +- src/coreclr/src/vm/gchelpers.cpp | 332 +++++++-------------- src/coreclr/src/vm/gchelpers.h | 8 +- src/coreclr/src/vm/i386/jitinterfacex86.cpp | 2 +- src/coreclr/src/vm/jithelpers.cpp | 4 +- src/coreclr/src/vm/object.h | 53 +--- src/coreclr/src/vm/object.inl | 18 -- src/coreclr/tests/issues.targets | 3 - .../src/GC/API/GC/AllocateUninitializedArray.cs | 151 ---------- .../GC/API/GC/AllocateUninitializedArray.csproj | 13 - src/libraries/System.Runtime/ref/System.Runtime.cs | 2 + .../System.Runtime/tests/System/GCTests.cs | 177 ++++++++++- .../System.Private.CoreLib/src/System/GC.Mono.cs | 17 +- 18 files changed, 406 insertions(+), 478 deletions(-) delete mode 100644 src/coreclr/tests/src/GC/API/GC/AllocateUninitializedArray.cs delete mode 100644 src/coreclr/tests/src/GC/API/GC/AllocateUninitializedArray.csproj diff --git a/src/coreclr/src/System.Private.CoreLib/ILLinkTrim.xml b/src/coreclr/src/System.Private.CoreLib/ILLinkTrim.xml index 1dc8146..ff08dc6 100644 --- a/src/coreclr/src/System.Private.CoreLib/ILLinkTrim.xml +++ b/src/coreclr/src/System.Private.CoreLib/ILLinkTrim.xml @@ -8,8 +8,6 @@ - - diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/GC.cs b/src/coreclr/src/System.Private.CoreLib/src/System/GC.cs index c71747c..fc8a965 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/GC.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/GC.cs @@ -84,8 +84,16 @@ namespace System [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] internal static extern int _EndNoGCRegion(); + // keep in sync with GC_ALLOC_FLAGS in gcinterface.h + internal enum GC_ALLOC_FLAGS + { + GC_ALLOC_NO_FLAGS = 0, + GC_ALLOC_ZEROING_OPTIONAL = 16, + GC_ALLOC_PINNED_OBJECT_HEAP = 64, + }; + [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Array AllocateNewArray(IntPtr typeHandle, int length, bool zeroingOptional); + internal static extern Array AllocateNewArray(IntPtr typeHandle, int length, GC_ALLOC_FLAGS flags); [MethodImpl(MethodImplOptions.InternalCall)] private static extern int GetGenerationWR(IntPtr handle); @@ -651,31 +659,74 @@ namespace System } /// - /// Skips zero-initialization of the array if possible. - /// If T contains object references, the array is always zero-initialized. + /// Allocate an array while skipping zero-initialization if possible. /// + /// Specifies the type of the array element. + /// Specifies the length of the array. + /// Specifies whether the allocated array must be pinned. + /// + /// If pinned is set to true, must not be a reference type or a type that contains object references. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] // forced to ensure no perf drop for small memory buffers (hot path) - internal static T[] AllocateUninitializedArray(int length) + public static T[] AllocateUninitializedArray(int length, bool pinned = false) { - if (RuntimeHelpers.IsReferenceOrContainsReferences()) + if (!pinned) { - return new T[length]; - } + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + return new T[length]; + } - // for debug builds we always want to call AllocateNewArray to detect AllocateNewArray bugs + // for debug builds we always want to call AllocateNewArray to detect AllocateNewArray bugs #if !DEBUG - // small arrays are allocated using `new[]` as that is generally faster. - if (length < 2048 / Unsafe.SizeOf()) + // small arrays are allocated using `new[]` as that is generally faster. + if (length < 2048 / Unsafe.SizeOf()) + { + return new T[length]; + } +#endif + } + else if (RuntimeHelpers.IsReferenceOrContainsReferences()) { - return new T[length]; + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); } -#endif + // kept outside of the small arrays hot path to have inlining without big size growth - return AllocateNewUninitializedArray(length); + return AllocateNewUninitializedArray(length, pinned); // remove the local function when https://github.com/dotnet/coreclr/issues/5329 is implemented - static T[] AllocateNewUninitializedArray(int length) - => Unsafe.As(AllocateNewArray(typeof(T[]).TypeHandle.Value, length, zeroingOptional: true)); + static T[] AllocateNewUninitializedArray(int length, bool pinned) + { + GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS.GC_ALLOC_ZEROING_OPTIONAL; + if (pinned) + flags |= GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP; + + return Unsafe.As(AllocateNewArray(typeof(T[]).TypeHandle.Value, length, flags)); + } + } + + /// + /// Allocate an array. + /// + /// Specifies the type of the array element. + /// Specifies the length of the array. + /// Specifies whether the allocated array must be pinned. + /// + /// If pinned is set to true, must not be a reference type or a type that contains object references. + /// + public static T[] AllocateArray(int length, bool pinned = false) + { + GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS.GC_ALLOC_NO_FLAGS; + + if (pinned) + { + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); + + flags = GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP; + } + + return Unsafe.As(AllocateNewArray(typeof(T[]).TypeHandle.Value, length, flags)); } } } diff --git a/src/coreclr/src/gc/gc.cpp b/src/coreclr/src/gc/gc.cpp index e547def..e54a993 100644 --- a/src/coreclr/src/gc/gc.cpp +++ b/src/coreclr/src/gc/gc.cpp @@ -12198,7 +12198,7 @@ void gc_heap::adjust_limit_clr (uint8_t* start, size_t limit_size, size_t size, uint8_t* obj_start = acontext->alloc_ptr; assert(start >= obj_start); uint8_t* obj_end = obj_start + size - plug_skew; - assert(obj_end > clear_start); + assert(obj_end >= clear_start); // if clearing at the object start, clear the syncblock. if(obj_start == start) @@ -37204,7 +37204,9 @@ GCHeap::Alloc(gc_alloc_context* context, size_t size, uint32_t flags REQD_ALIGN_ #endif //_PREFAST_ #endif //MULTIPLE_HEAPS - if (size >= loh_size_threshold || (flags & GC_ALLOC_LARGE_OBJECT_HEAP)) + assert(size < loh_size_threshold || (flags & GC_ALLOC_LARGE_OBJECT_HEAP)); + + if (flags & GC_ALLOC_USER_OLD_HEAP) { // The LOH always guarantees at least 8-byte alignment, regardless of platform. Moreover it doesn't // support mis-aligned object headers so we can't support biased headers. Luckily for us @@ -37213,7 +37215,8 @@ GCHeap::Alloc(gc_alloc_context* context, size_t size, uint32_t flags REQD_ALIGN_ ASSERT((flags & GC_ALLOC_ALIGN8_BIAS) == 0); ASSERT(65536 < loh_size_threshold); - newAlloc = (Object*) hp->allocate_uoh_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), flags, loh_generation, acontext->alloc_bytes_uoh); + int gen_num = (flags & GC_ALLOC_PINNED_OBJECT_HEAP) ? poh_generation : loh_generation; + newAlloc = (Object*) hp->allocate_uoh_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), flags, gen_num, acontext->alloc_bytes_uoh); ASSERT(((size_t)newAlloc & 7) == 0); #ifdef FEATURE_STRUCTALIGN diff --git a/src/coreclr/src/gc/gcinterface.h b/src/coreclr/src/gc/gcinterface.h index cd78561..de6456d 100644 --- a/src/coreclr/src/gc/gcinterface.h +++ b/src/coreclr/src/gc/gcinterface.h @@ -890,7 +890,7 @@ void updateGCShadow(Object** ptr, Object* val); #define GC_CALL_INTERIOR 0x1 #define GC_CALL_PINNED 0x2 -//flags for IGCHeapAlloc(...) +// keep in sync with GC_ALLOC_FLAGS in GC.cs enum GC_ALLOC_FLAGS { GC_ALLOC_NO_FLAGS = 0, @@ -901,6 +901,7 @@ enum GC_ALLOC_FLAGS GC_ALLOC_ZEROING_OPTIONAL = 16, GC_ALLOC_LARGE_OBJECT_HEAP = 32, GC_ALLOC_PINNED_OBJECT_HEAP = 64, + GC_ALLOC_USER_OLD_HEAP = GC_ALLOC_LARGE_OBJECT_HEAP | GC_ALLOC_PINNED_OBJECT_HEAP, }; inline GC_ALLOC_FLAGS operator|(GC_ALLOC_FLAGS a, GC_ALLOC_FLAGS b) diff --git a/src/coreclr/src/vm/comutilnative.cpp b/src/coreclr/src/vm/comutilnative.cpp index 7d0dea1..1f0e257 100644 --- a/src/coreclr/src/vm/comutilnative.cpp +++ b/src/coreclr/src/vm/comutilnative.cpp @@ -1047,7 +1047,7 @@ FCIMPLEND ** zeroingOptional -> whether caller prefers to skip clearing the content of the array, if possible. **Exceptions: IDS_EE_ARRAY_DIMENSIONS_EXCEEDED when size is too large. OOM if can't allocate. ==============================================================================*/ -FCIMPL3(Object*, GCInterface::AllocateNewArray, void* arrayTypeHandle, INT32 length, CLR_BOOL zeroingOptional) +FCIMPL3(Object*, GCInterface::AllocateNewArray, void* arrayTypeHandle, INT32 length, INT32 flags) { CONTRACTL { FCALL_CHECK; @@ -1058,7 +1058,10 @@ FCIMPL3(Object*, GCInterface::AllocateNewArray, void* arrayTypeHandle, INT32 len HELPER_METHOD_FRAME_BEGIN_RET_0(); - pRet = AllocateSzArray(arrayType, length, zeroingOptional ? GC_ALLOC_ZEROING_OPTIONAL : GC_ALLOC_NO_FLAGS); + //Only the following flags are used by GC.cs, so we'll just assert it here. + _ASSERTE((flags & ~(GC_ALLOC_ZEROING_OPTIONAL | GC_ALLOC_PINNED_OBJECT_HEAP)) == 0); + + pRet = AllocateSzArray(arrayType, length, (GC_ALLOC_FLAGS)flags); HELPER_METHOD_FRAME_END(); diff --git a/src/coreclr/src/vm/comutilnative.h b/src/coreclr/src/vm/comutilnative.h index 56030c4..4493ec2 100644 --- a/src/coreclr/src/vm/comutilnative.h +++ b/src/coreclr/src/vm/comutilnative.h @@ -128,7 +128,7 @@ public: static FCDECL0(INT64, GetAllocatedBytesForCurrentThread); static FCDECL1(INT64, GetTotalAllocatedBytes, CLR_BOOL precise); - static FCDECL3(Object*, AllocateNewArray, void* elementTypeHandle, INT32 length, CLR_BOOL zeroingOptional); + static FCDECL3(Object*, AllocateNewArray, void* elementTypeHandle, INT32 length, INT32 flags); #ifdef FEATURE_BASICFREEZE static diff --git a/src/coreclr/src/vm/gchelpers.cpp b/src/coreclr/src/vm/gchelpers.cpp index e9352bd..5f375da 100644 --- a/src/coreclr/src/vm/gchelpers.cpp +++ b/src/coreclr/src/vm/gchelpers.cpp @@ -42,14 +42,6 @@ // //======================================================================== -#define ProfileTrackArrayAlloc(orObject) \ - OBJECTREF objref = ObjectToOBJECTREF((Object*)orObject);\ - GCPROTECT_BEGIN(objref);\ - ProfilerObjectAllocatedCallback(objref, (ClassID) orObject->GetTypeHandle().AsPtr());\ - GCPROTECT_END();\ - orObject = (ArrayBase *) OBJECTREFToObject(objref); - - inline gc_alloc_context* GetThreadAllocContext() { WRAPPER_NO_CONTRACT; @@ -281,7 +273,7 @@ bool ToLogOrNotToLog(size_t size, const char *typeName) // this function is called on managed allocation path with unprotected Object* // as a result LogAlloc cannot call anything that would toggle the GC mode else // you'll introduce several GC holes! -inline void LogAlloc(size_t size, MethodTable *pMT, Object* object) +inline void LogAlloc(Object* object) { CONTRACTL { @@ -292,6 +284,9 @@ inline void LogAlloc(size_t size, MethodTable *pMT, Object* object) CONTRACTL_END; #ifdef LOGGING + MethodTable* pMT = object->GetMethodTable(); + size_t size = object->GetSize(); + if (LoggingOn(LF_GCALLOC, LL_INFO10)) { LogSpewAlways("Allocated %5d bytes for %s_TYPE" FMT_ADDR FMT_CLASS "\n", @@ -311,9 +306,44 @@ inline void LogAlloc(size_t size, MethodTable *pMT, Object* object) #endif } #else -#define LogAlloc(size, pMT, object) +#define LogAlloc( object) #endif +// signals completion of the object to GC and sends events if necessary +template +void PublishObjectAndNotify(TObj* &orObject, GC_ALLOC_FLAGS flags) +{ + _ASSERTE(orObject->HasEmptySyncBlockInfo()); + + if (flags & GC_ALLOC_USER_OLD_HEAP) + { + GCHeapUtilities::GetGCHeap()->PublishObject((BYTE*)orObject); + } + +#ifdef _LOGALLOC + LogAlloc(orObject); +#endif // _LOGALLOC + + // Notify the profiler of the allocation + // do this after initializing bounds so callback has size information + if (TrackAllocations() || + (TrackLargeAllocations() && flags & GC_ALLOC_LARGE_OBJECT_HEAP)) + { + OBJECTREF objref = ObjectToOBJECTREF((Object*)orObject); + GCPROTECT_BEGIN(objref); + ProfilerObjectAllocatedCallback(objref, (ClassID) orObject->GetTypeHandle().AsPtr()); + GCPROTECT_END(); + orObject = (TObj*) OBJECTREFToObject(objref); + } + +#ifdef FEATURE_EVENT_TRACE + // Send ETW event for allocation + if(ETW::TypeSystemLog::IsHeapAllocEventEnabled()) + { + ETW::TypeSystemLog::SendObjectAllocatedEvent(orObject); + } +#endif // FEATURE_EVENT_TRACE +} inline SIZE_T MaxArrayLength(SIZE_T componentSize) { @@ -324,7 +354,7 @@ inline SIZE_T MaxArrayLength(SIZE_T componentSize) return (componentSize == 1) ? 0X7FFFFFC7 : 0X7FEFFFFF; } -OBJECTREF AllocateSzArray(TypeHandle arrayType, INT32 cElements, GC_ALLOC_FLAGS flags, BOOL bAllocateInLargeHeap) +OBJECTREF AllocateSzArray(TypeHandle arrayType, INT32 cElements, GC_ALLOC_FLAGS flags) { CONTRACTL{ THROWS; @@ -334,10 +364,10 @@ OBJECTREF AllocateSzArray(TypeHandle arrayType, INT32 cElements, GC_ALLOC_FLAGS MethodTable* pArrayMT = arrayType.AsMethodTable(); - return AllocateSzArray(pArrayMT, cElements, flags, bAllocateInLargeHeap); + return AllocateSzArray(pArrayMT, cElements, flags); } -OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS flags, BOOL bAllocateInLargeHeap) +OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS flags) { CONTRACTL{ THROWS; @@ -345,6 +375,8 @@ OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS MODE_COOPERATIVE; // returns an objref without pinning it => cooperative } CONTRACTL_END; + // IBC Log MethodTable access + g_IBCLogger.LogMethodTableAccess(pArrayMT); SetTypeHandleOnThreadForAlloc(TypeHandle(pArrayMT)); _ASSERTE(pArrayMT->CheckInstanceActivated()); @@ -356,9 +388,6 @@ OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS if (elemType == ELEMENT_TYPE_VOID) COMPlusThrow(kArgumentException); - // IBC Log MethodTable access - g_IBCLogger.LogMethodTableAccess(pArrayMT); - if (cElements < 0) COMPlusThrow(kOverflowException); @@ -385,22 +414,21 @@ OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS ((DWORD)cElements >= g_pConfig->GetDoubleArrayToLargeObjectHeapThreshold())) { STRESS_LOG2(LF_GC, LL_INFO10, "Allocating double MD array of size %d and length %d to large object heap\n", totalSize, cElements); - bAllocateInLargeHeap = TRUE; + flags |= GC_ALLOC_LARGE_OBJECT_HEAP; } #endif if (totalSize >= g_pConfig->GetGCLOHThreshold()) - { - bAllocateInLargeHeap = TRUE; - } + flags |= GC_ALLOC_LARGE_OBJECT_HEAP; - flags |= (pArrayMT->ContainsPointers() ? GC_ALLOC_CONTAINS_REF : GC_ALLOC_NO_FLAGS); + if (pArrayMT->ContainsPointers()) + flags |= GC_ALLOC_CONTAINS_REF; ArrayBase* orArray = NULL; - if (bAllocateInLargeHeap) + if (flags & GC_ALLOC_USER_OLD_HEAP) { - orArray = (ArrayBase*)Alloc(totalSize, flags | GC_ALLOC_LARGE_OBJECT_HEAP); - orArray->SetArrayMethodTableForLargeObject(pArrayMT); + orArray = (ArrayBase*)Alloc(totalSize, flags); + orArray->SetMethodTableForUOHObject(pArrayMT); } else { @@ -457,40 +485,14 @@ OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS #endif orArray = (ArrayBase*)Alloc(totalSize, flags); } - orArray->SetArrayMethodTable(pArrayMT); + orArray->SetMethodTable(pArrayMT); } // Initialize Object orArray->m_NumComponents = cElements; - bool bProfilerNotifyLargeAllocation = false; - - if (bAllocateInLargeHeap) - { - GCHeapUtilities::GetGCHeap()->PublishObject((BYTE*)orArray); - bProfilerNotifyLargeAllocation = TrackLargeAllocations(); - } - -#ifdef _LOGALLOC - LogAlloc(totalSize, pArrayMT, orArray); -#endif // _LOGALLOC - - // Notify the profiler of the allocation - // do this after initializing bounds so callback has size information - if (TrackAllocations() || bProfilerNotifyLargeAllocation) - { - ProfileTrackArrayAlloc(orArray); - } - -#ifdef FEATURE_EVENT_TRACE - // Send ETW event for allocation - if(ETW::TypeSystemLog::IsHeapAllocEventEnabled()) - { - ETW::TypeSystemLog::SendObjectAllocatedEvent(orArray); - } -#endif // FEATURE_EVENT_TRACE - - return ObjectToOBJECTREF((Object *) orArray); + PublishObjectAndNotify(orArray, flags); + return ObjectToOBJECTREF((Object*)orArray); } void ThrowOutOfMemoryDimensionsExceeded() @@ -511,7 +513,7 @@ void ThrowOutOfMemoryDimensionsExceeded() // // This is wrapper overload to handle TypeHandle arrayType // -OBJECTREF AllocateArrayEx(TypeHandle arrayType, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags, BOOL bAllocateInLargeHeap) +OBJECTREF AllocateArrayEx(TypeHandle arrayType, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags) { CONTRACTL { @@ -520,7 +522,7 @@ OBJECTREF AllocateArrayEx(TypeHandle arrayType, INT32 *pArgs, DWORD dwNumArgs, G MethodTable* pArrayMT = arrayType.AsMethodTable(); - return AllocateArrayEx(pArrayMT, pArgs, dwNumArgs, flags, bAllocateInLargeHeap); + return AllocateArrayEx(pArrayMT, pArgs, dwNumArgs, flags); } // @@ -530,7 +532,7 @@ OBJECTREF AllocateArrayEx(TypeHandle arrayType, INT32 *pArgs, DWORD dwNumArgs, G // allocate sub-arrays and fill them in. // // For arrays with lower bounds, pBounds is , , , ... -OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags, BOOL bAllocateInLargeHeap) +OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags) { CONTRACTL { THROWS; @@ -540,8 +542,6 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, PRECONDITION(dwNumArgs > 0); } CONTRACTL_END; - ArrayBase * orArray = NULL; - #ifdef _DEBUG if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP)) { @@ -550,6 +550,15 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, } #endif + // IBC Log MethodTable access + g_IBCLogger.LogMethodTableAccess(pArrayMT); + SetTypeHandleOnThreadForAlloc(TypeHandle(pArrayMT)); + + // keep original flags in case the call is recursive (jugged array case) + // the aditional flags that we infer here, such as GC_ALLOC_CONTAINS_REF + // may not be applicable to inner arrays + GC_ALLOC_FLAGS flagsOriginal = flags; + _ASSERTE(pArrayMT->CheckInstanceActivated()); PREFIX_ASSUME(pArrayMT != NULL); CorElementType kind = pArrayMT->GetInternalCorElementType(); @@ -562,11 +571,6 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, // Calculate the total number of elements in the array UINT32 cElements; - - // IBC Log MethodTable access - g_IBCLogger.LogMethodTableAccess(pArrayMT); - SetTypeHandleOnThreadForAlloc(TypeHandle(pArrayMT)); - SIZE_T componentSize = pArrayMT->GetComponentSize(); bool maxArrayDimensionLengthOverflow = false; bool providedLowerBounds = false; @@ -580,7 +584,7 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, if (rank == 1 && (dwNumArgs == 1 || pArgs[0] == 0)) { TypeHandle szArrayType = ClassLoader::LoadArrayTypeThrowing(pArrayMT->GetArrayElementTypeHandle(), ELEMENT_TYPE_SZARRAY, 1); - return AllocateSzArray(szArrayType, pArgs[dwNumArgs - 1], flags, bAllocateInLargeHeap); + return AllocateSzArray(szArrayType, pArgs[dwNumArgs - 1], flags); } providedLowerBounds = (dwNumArgs == 2*rank); @@ -642,21 +646,21 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, (cElements >= g_pConfig->GetDoubleArrayToLargeObjectHeapThreshold())) { STRESS_LOG2(LF_GC, LL_INFO10, "Allocating double MD array of size %d and length %d to large object heap\n", totalSize, cElements); - bAllocateInLargeHeap = TRUE; + flags |= GC_ALLOC_LARGE_OBJECT_HEAP; } #endif if (totalSize >= g_pConfig->GetGCLOHThreshold()) - { - bAllocateInLargeHeap = TRUE; - } + flags |= GC_ALLOC_LARGE_OBJECT_HEAP; - flags |= (pArrayMT->ContainsPointers() ? GC_ALLOC_CONTAINS_REF : GC_ALLOC_NO_FLAGS); + if (pArrayMT->ContainsPointers()) + flags |= GC_ALLOC_CONTAINS_REF; - if (bAllocateInLargeHeap) + ArrayBase* orArray = NULL; + if (flags & GC_ALLOC_USER_OLD_HEAP) { - orArray = (ArrayBase *) Alloc(totalSize, flags | GC_ALLOC_LARGE_OBJECT_HEAP); - orArray->SetArrayMethodTableForLargeObject(pArrayMT); + orArray = (ArrayBase*)Alloc(totalSize, flags); + orArray->SetMethodTableForUOHObject(pArrayMT); } else { @@ -674,24 +678,11 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, } #endif orArray = (ArrayBase*)Alloc(totalSize, flags); - orArray->SetArrayMethodTable(pArrayMT); + orArray->SetMethodTable(pArrayMT); } // Initialize Object orArray->m_NumComponents = cElements; - - bool bProfilerNotifyLargeAllocation = false; - - if (bAllocateInLargeHeap) - { - GCHeapUtilities::GetGCHeap()->PublishObject((BYTE*)orArray); - bProfilerNotifyLargeAllocation = TrackLargeAllocations(); - } - -#ifdef _LOGALLOC - LogAlloc(totalSize, pArrayMT, orArray); -#endif // _LOGALLOC - if (kind == ELEMENT_TYPE_ARRAY) { INT32 *pCountsPtr = (INT32 *) orArray->GetBoundsPtr(); @@ -704,20 +695,7 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, } } - // Notify the profiler of the allocation - // do this after initializing bounds so callback has size information - if (TrackAllocations() || bProfilerNotifyLargeAllocation) - { - ProfileTrackArrayAlloc(orArray); - } - -#ifdef FEATURE_EVENT_TRACE - // Send ETW event for allocation - if(ETW::TypeSystemLog::IsHeapAllocEventEnabled()) - { - ETW::TypeSystemLog::SendObjectAllocatedEvent(orArray); - } -#endif // FEATURE_EVENT_TRACE + PublishObjectAndNotify(orArray, flags); if (kind != ELEMENT_TYPE_ARRAY) { @@ -741,7 +719,7 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, TypeHandle subArrayType = pArrayMT->GetArrayElementTypeHandle(); for (UINT32 i = 0; i < cElements; i++) { - OBJECTREF obj = AllocateArrayEx(subArrayType, &pArgs[1], dwNumArgs-1, flags, bAllocateInLargeHeap); + OBJECTREF obj = AllocateArrayEx(subArrayType, &pArgs[1], dwNumArgs-1, flagsOriginal); outerArray->SetAt(i, obj); } @@ -848,7 +826,8 @@ OBJECTREF AllocateObjectArray(DWORD cElements, TypeHandle elementType, BOOL bAll _ASSERTE(arrayType.GetInternalCorElementType() == ELEMENT_TYPE_SZARRAY); #endif //_DEBUG - return AllocateSzArray(arrayType, (INT32) cElements, GC_ALLOC_NO_FLAGS, bAllocateInLargeHeap); + GC_ALLOC_FLAGS flags = bAllocateInLargeHeap ? GC_ALLOC_LARGE_OBJECT_HEAP : GC_ALLOC_NO_FLAGS; + return AllocateSzArray(arrayType, (INT32) cElements, flags); } STRINGREF AllocateString( DWORD cchStringLength ) @@ -859,8 +838,6 @@ STRINGREF AllocateString( DWORD cchStringLength ) MODE_COOPERATIVE; // returns an objref without pinning it => cooperative } CONTRACTL_END; - StringObject *orObject = NULL; - #ifdef _DEBUG if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP)) { @@ -876,50 +853,23 @@ STRINGREF AllocateString( DWORD cchStringLength ) if (cchStringLength > 0x3FFFFFDF) ThrowOutOfMemory(); - SIZE_T ObjectSize = PtrAlign(StringObject::GetSize(cchStringLength)); - _ASSERTE(ObjectSize > cchStringLength); + SIZE_T totalSize = PtrAlign(StringObject::GetSize(cchStringLength)); + _ASSERTE(totalSize > cchStringLength); SetTypeHandleOnThreadForAlloc(TypeHandle(g_pStringClass)); - orObject = (StringObject *)Alloc( ObjectSize, GC_ALLOC_NO_FLAGS); + GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS; + if (totalSize >= g_pConfig->GetGCLOHThreshold()) + flags |= GC_ALLOC_LARGE_OBJECT_HEAP; - // Object is zero-init already - _ASSERTE( orObject->HasEmptySyncBlockInfo() ); + StringObject* orString = (StringObject*)Alloc(totalSize, flags); // Initialize Object - //@TODO need to build a LARGE g_pStringMethodTable before - orObject->SetMethodTable( g_pStringClass ); - orObject->SetStringLength( cchStringLength ); - - bool bProfilerNotifyLargeAllocation = false; - if (ObjectSize >= g_pConfig->GetGCLOHThreshold()) - { - bProfilerNotifyLargeAllocation = TrackLargeAllocations(); - GCHeapUtilities::GetGCHeap()->PublishObject((BYTE*)orObject); - } + orString->SetMethodTable(g_pStringClass); + orString->SetStringLength(cchStringLength); - // Notify the profiler of the allocation - if (TrackAllocations() || bProfilerNotifyLargeAllocation) - { - OBJECTREF objref = ObjectToOBJECTREF((Object*)orObject); - GCPROTECT_BEGIN(objref); - ProfilerObjectAllocatedCallback(objref, (ClassID) orObject->GetTypeHandle().AsPtr()); - GCPROTECT_END(); - - orObject = (StringObject *) OBJECTREFToObject(objref); - } - -#ifdef FEATURE_EVENT_TRACE - // Send ETW event for allocation - if(ETW::TypeSystemLog::IsHeapAllocEventEnabled()) - { - ETW::TypeSystemLog::SendObjectAllocatedEvent(orObject); - } -#endif // FEATURE_EVENT_TRACE - - LogAlloc(ObjectSize, g_pStringClass, orObject); - - return( ObjectToSTRINGREF(orObject) ); + PublishObjectAndNotify(orString, flags); + return ObjectToSTRINGREF(orString); } #ifdef FEATURE_UTF8STRING @@ -931,8 +881,6 @@ UTF8STRINGREF AllocateUtf8String(DWORD cchStringLength) MODE_COOPERATIVE; // returns an objref without pinning it => cooperative } CONTRACTL_END; - Utf8StringObject *orObject = NULL; - #ifdef _DEBUG if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP)) { @@ -953,50 +901,23 @@ UTF8STRINGREF AllocateUtf8String(DWORD cchStringLength) if (cchStringLength > 0x7FFFFFBF) ThrowOutOfMemory(); - SIZE_T ObjectSize = PtrAlign(Utf8StringObject::GetSize(cchStringLength)); - _ASSERTE(ObjectSize > cchStringLength); + SIZE_T totalSize = PtrAlign(Utf8StringObject::GetSize(cchStringLength)); + _ASSERTE(totalSize > cchStringLength); SetTypeHandleOnThreadForAlloc(TypeHandle(g_pUtf8StringClass)); - orObject = (Utf8StringObject *)Alloc(ObjectSize, GC_ALLOC_NO_FLAGS); + GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS; + if (totalSize >= g_pConfig->GetGCLOHThreshold()) + flags |= GC_ALLOC_LARGE_OBJECT_HEAP; - // Object is zero-init already - _ASSERTE(orObject->HasEmptySyncBlockInfo()); + Utf8StringObject* orString = (Utf8StringObject*)Alloc(totalSize, flags); // Initialize Object - orObject->SetMethodTable(g_pUtf8StringClass); - orObject->SetLength(cchStringLength); - - bool bProfilerNotifyLargeAllocation = false; - - if (ObjectSize >= g_pConfig->GetGCLOHThreshold()) - { - GCHeapUtilities::GetGCHeap()->PublishObject((BYTE*)orObject); - bProfilerNotifyLargeAllocation = TrackLargeAllocations(); - } - - // Notify the profiler of the allocation - if (TrackAllocations() || bProfilerNotifyLargeAllocation) - { - OBJECTREF objref = ObjectToOBJECTREF((Object*)orObject); - GCPROTECT_BEGIN(objref); - ProfilerObjectAllocatedCallback(objref, (ClassID)orObject->GetTypeHandle().AsPtr()); - GCPROTECT_END(); - - orObject = (Utf8StringObject *)OBJECTREFToObject(objref); - } + orString->SetMethodTable(g_pUtf8StringClass); + orString->SetLength(cchStringLength); -#ifdef FEATURE_EVENT_TRACE - // Send ETW event for allocation - if (ETW::TypeSystemLog::IsHeapAllocEventEnabled()) - { - ETW::TypeSystemLog::SendObjectAllocatedEvent(orObject); - } -#endif // FEATURE_EVENT_TRACE - - LogAlloc(ObjectSize, g_pUtf8StringClass, orObject); - - return( ObjectToUTF8STRINGREF(orObject) ); + PublishObjectAndNotify(orString, flags); + return ObjectToUTF8STRINGREF(orString); } #endif // FEATURE_UTF8STRING @@ -1073,9 +994,16 @@ OBJECTREF AllocateObject(MethodTable *pMT #endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION #endif // FEATURE_COMINTEROP { - DWORD baseSize = pMT->GetBaseSize(); - GC_ALLOC_FLAGS flags = ((pMT->ContainsPointers() ? GC_ALLOC_CONTAINS_REF : GC_ALLOC_NO_FLAGS) | - (pMT->HasFinalizer() ? GC_ALLOC_FINALIZE : GC_ALLOC_NO_FLAGS)); + GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS; + if (pMT->ContainsPointers()) + flags |= GC_ALLOC_CONTAINS_REF; + + if (pMT->HasFinalizer()) + flags |= GC_ALLOC_FINALIZE; + + DWORD totalSize = pMT->GetBaseSize(); + if (totalSize >= g_pConfig->GetGCLOHThreshold()) + flags |= GC_ALLOC_LARGE_OBJECT_HEAP; #ifdef FEATURE_64BIT_ALIGNMENT if (pMT->RequiresAlign8()) @@ -1089,50 +1017,22 @@ OBJECTREF AllocateObject(MethodTable *pMT _ASSERTE(sizeof(Object) == 4); flags |= GC_ALLOC_ALIGN8; if (pMT->IsValueType()) - { flags |= GC_ALLOC_ALIGN8_BIAS; - } } #endif // FEATURE_64BIT_ALIGNMENT - Object* orObject = (Object*)Alloc(baseSize, flags); - - // verify zero'd memory (at least for sync block) - _ASSERTE( orObject->HasEmptySyncBlockInfo() ); + Object* orObject = (Object*)Alloc(totalSize, flags); - bool bProfilerNotifyLargeAllocation = false; - if ((baseSize >= g_pConfig->GetGCLOHThreshold())) + if (flags & GC_ALLOC_USER_OLD_HEAP) { - orObject->SetMethodTableForLargeObject(pMT); - bProfilerNotifyLargeAllocation = TrackLargeAllocations(); - GCHeapUtilities::GetGCHeap()->PublishObject((BYTE*)orObject); + orObject->SetMethodTableForUOHObject(pMT); } else { orObject->SetMethodTable(pMT); } - // Notify the profiler of the allocation - if (TrackAllocations() || bProfilerNotifyLargeAllocation) - { - OBJECTREF objref = ObjectToOBJECTREF((Object*)orObject); - GCPROTECT_BEGIN(objref); - ProfilerObjectAllocatedCallback(objref, (ClassID) orObject->GetTypeHandle().AsPtr()); - GCPROTECT_END(); - - orObject = (Object *) OBJECTREFToObject(objref); - } - -#ifdef FEATURE_EVENT_TRACE - // Send ETW event for allocation - if(ETW::TypeSystemLog::IsHeapAllocEventEnabled()) - { - ETW::TypeSystemLog::SendObjectAllocatedEvent(orObject); - } -#endif // FEATURE_EVENT_TRACE - - LogAlloc(pMT->GetBaseSize(), pMT, orObject); - + PublishObjectAndNotify(orObject, flags); oref = OBJECTREF_TO_UNCHECKED_OBJECTREF(orObject); } @@ -1466,7 +1366,7 @@ void ErectWriteBarrierForMT(MethodTable **dst, MethodTable *ref) #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP - BYTE *refObject = *(BYTE **)((MethodTable*)ref)->GetLoaderAllocatorObjectHandle(); + BYTE *refObject = *(BYTE **)ref->GetLoaderAllocatorObjectHandle(); if((BYTE*) refObject >= g_ephemeral_low && (BYTE*) refObject < g_ephemeral_high) { // VolatileLoadWithoutBarrier() is used here to prevent fetch of g_card_table from being reordered diff --git a/src/coreclr/src/vm/gchelpers.h b/src/coreclr/src/vm/gchelpers.h index 5fe51ce..9cf70b1 100644 --- a/src/coreclr/src/vm/gchelpers.h +++ b/src/coreclr/src/vm/gchelpers.h @@ -21,12 +21,12 @@ //======================================================================== // Allocate single-dimensional array given array type -OBJECTREF AllocateSzArray(MethodTable *pArrayMT, INT32 length, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS, BOOL bAllocateInLargeHeap = FALSE); -OBJECTREF AllocateSzArray(TypeHandle arrayType, INT32 length, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS, BOOL bAllocateInLargeHeap = FALSE); +OBJECTREF AllocateSzArray(MethodTable *pArrayMT, INT32 length, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS); +OBJECTREF AllocateSzArray(TypeHandle arrayType, INT32 length, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS); // The main Array allocation routine, can do multi-dimensional -OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS, BOOL bAllocateInLargeHeap = FALSE); -OBJECTREF AllocateArrayEx(TypeHandle arrayType, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS, BOOL bAllocateInLargeHeap = FALSE); +OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS); +OBJECTREF AllocateArrayEx(TypeHandle arrayType, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS); // Create a SD array of primitive types given an element type OBJECTREF AllocatePrimitiveArray(CorElementType type, DWORD cElements); diff --git a/src/coreclr/src/vm/i386/jitinterfacex86.cpp b/src/coreclr/src/vm/i386/jitinterfacex86.cpp index 5666687..edc3fa1 100644 --- a/src/coreclr/src/vm/i386/jitinterfacex86.cpp +++ b/src/coreclr/src/vm/i386/jitinterfacex86.cpp @@ -717,7 +717,7 @@ void *JIT_TrialAlloc::GenAllocString(Flags flags) // we need to load the method table for string from the global - // mov ecx, [g_pStringMethodTable] + // mov ecx, [g_pStringClass] sl.Emit16(0x0d8b); sl.Emit32((int)(size_t)&g_pStringClass); diff --git a/src/coreclr/src/vm/jithelpers.cpp b/src/coreclr/src/vm/jithelpers.cpp index 0f3d4f3..8d64b26 100644 --- a/src/coreclr/src/vm/jithelpers.cpp +++ b/src/coreclr/src/vm/jithelpers.cpp @@ -2609,7 +2609,7 @@ HCIMPL2(Object*, JIT_NewArr1VC_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, IN _ASSERTE(allocPtr != nullptr); ArrayBase *array = reinterpret_cast(allocPtr); - array->SetArrayMethodTable(pArrayMT); + array->SetMethodTable(pArrayMT); _ASSERTE(static_cast(componentCount) == componentCount); array->m_NumComponents = static_cast(componentCount); @@ -2668,7 +2668,7 @@ HCIMPL2(Object*, JIT_NewArr1OBJ_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, I _ASSERTE(allocPtr != nullptr); ArrayBase *array = reinterpret_cast(allocPtr); - array->SetArrayMethodTable(pArrayMT); + array->SetMethodTable(pArrayMT); _ASSERTE(static_cast(componentCount) == componentCount); array->m_NumComponents = static_cast(componentCount); diff --git a/src/coreclr/src/vm/object.h b/src/coreclr/src/vm/object.h index a7d68c1..8a09151 100644 --- a/src/coreclr/src/vm/object.h +++ b/src/coreclr/src/vm/object.h @@ -149,33 +149,17 @@ class Object m_pMethTab = pMT; } - VOID SetMethodTable(MethodTable *pMT - DEBUG_ARG(BOOL bAllowArray = FALSE)) + VOID SetMethodTable(MethodTable *pMT) { - LIMITED_METHOD_CONTRACT; - m_pMethTab = pMT; - -#ifdef _DEBUG - if (!bAllowArray) - { - AssertNotArray(); - } -#endif // _DEBUG + WRAPPER_NO_CONTRACT; + RawSetMethodTable(pMT); } - VOID SetMethodTableForLargeObject(MethodTable *pMT - DEBUG_ARG(BOOL bAllowArray = FALSE)) + VOID SetMethodTableForUOHObject(MethodTable *pMT) { - // This function must be used if the allocation occurs on the large object heap, and the method table might be a collectible type WRAPPER_NO_CONTRACT; + // This function must be used if the allocation occurs on a UOH heap, and the method table might be a collectible type ErectWriteBarrierForMT(&m_pMethTab, pMT); - -#ifdef _DEBUG - if (!bAllowArray) - { - AssertNotArray(); - } -#endif // _DEBUG } #endif //!DACCESS_COMPILE @@ -477,16 +461,6 @@ class Object private: VOID ValidateInner(BOOL bDeep, BOOL bVerifyNextHeader, BOOL bVerifySyncBlock); - -#ifdef _DEBUG - void AssertNotArray() - { - if (m_pMethTab->IsArray()) - { - _ASSERTE(!"ArrayBase::SetArrayMethodTable/ArrayBase::SetArrayMethodTableForLargeObject should be used for arrays"); - } - } -#endif // _DEBUG }; /* @@ -547,8 +521,8 @@ class ArrayBase : public Object friend class GCHeap; friend class CObjectHeader; friend class Object; - friend OBJECTREF AllocateSzArray(MethodTable *pArrayMT, INT32 length, GC_ALLOC_FLAGS flags, BOOL bAllocateInLargeHeap); - friend OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags, BOOL bAllocateInLargeHeap); + friend OBJECTREF AllocateSzArray(MethodTable *pArrayMT, INT32 length, GC_ALLOC_FLAGS flags); + friend OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags); friend FCDECL2(Object*, JIT_NewArr1VC_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size); friend FCDECL2(Object*, JIT_NewArr1OBJ_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size); friend class JIT_TrialAlloc; @@ -575,20 +549,15 @@ public: // type is stored in the array or not inline TypeHandle GetArrayElementTypeHandle() const; - // Get the CorElementType for the elements in the array. Avoids creating a TypeHandle + // Get the CorElementType for the elements in the array. Avoids creating a TypeHandle inline CorElementType GetArrayElementType() const; inline unsigned GetRank() const; - // Total element count for the array + // Total element count for the array inline DWORD GetNumComponents() const; -#ifndef DACCESS_COMPILE - inline void SetArrayMethodTable(MethodTable *pArrayMT); - inline void SetArrayMethodTableForLargeObject(MethodTable *pArrayMT); -#endif // !DACCESS_COMPILE - - // Get pointer to elements, handles any number of dimensions + // Get pointer to elements, handles any number of dimensions PTR_BYTE GetDataPtr(BOOL inGC = FALSE) const { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; @@ -699,7 +668,7 @@ class PtrArray : public ArrayBase { friend class GCHeap; friend class ClrDataAccess; - friend OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, DWORD flags, BOOL bAllocateInLargeHeap); + friend OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, DWORD flags); friend class JIT_TrialAlloc; friend class CheckAsmOffsets; diff --git a/src/coreclr/src/vm/object.inl b/src/coreclr/src/vm/object.inl index 8ed202d..cac1582 100644 --- a/src/coreclr/src/vm/object.inl +++ b/src/coreclr/src/vm/object.inl @@ -175,24 +175,6 @@ inline DWORD ArrayBase::GetNumComponents() const return m_NumComponents; } -#ifndef DACCESS_COMPILE -inline void ArrayBase::SetArrayMethodTable(MethodTable *pArrayMT) -{ - LIMITED_METHOD_CONTRACT; - - SetMethodTable(pArrayMT - DEBUG_ARG(TRUE)); -} - -inline void ArrayBase::SetArrayMethodTableForLargeObject(MethodTable *pArrayMT) -{ - LIMITED_METHOD_CONTRACT; - - SetMethodTableForLargeObject(pArrayMT - DEBUG_ARG(TRUE)); -} -#endif // !DACCESS_COMPILE - inline /* static */ unsigned ArrayBase::GetDataPtrOffset(MethodTable* pMT) { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/tests/issues.targets b/src/coreclr/tests/issues.targets index 47b7d78..b5c8ba6 100644 --- a/src/coreclr/tests/issues.targets +++ b/src/coreclr/tests/issues.targets @@ -997,9 +997,6 @@ needs triage - - needs triage - needs triage diff --git a/src/coreclr/tests/src/GC/API/GC/AllocateUninitializedArray.cs b/src/coreclr/tests/src/GC/API/GC/AllocateUninitializedArray.cs deleted file mode 100644 index 28dcd96..0000000 --- a/src/coreclr/tests/src/GC/API/GC/AllocateUninitializedArray.cs +++ /dev/null @@ -1,151 +0,0 @@ -// 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. -// Tests GC.Collect() - -using System; - -public class Test { - - - public static int Main() { - // allocate a bunch of SOH byte arrays and touch them. - var r = new Random(1234); - for (int i = 0; i < 10000; i++) - { - int size = r.Next(10000); - var arr = AllocUninitialized.Call(size); - - if (size > 1) - { - arr[0] = 5; - arr[size - 1] = 17; - if (arr[0] != 5 || arr[size - 1] != 17) - { - Console.WriteLine("Scenario 1 for GC.AllocUninitialized() failed!"); - return 1; - } - } - } - - // allocate a bunch of LOH int arrays and touch them. - for (int i = 0; i < 1000; i++) - { - int size = r.Next(100000, 1000000); - var arr = AllocUninitialized.Call(size); - - arr[0] = 5; - arr[size - 1] = 17; - if (arr[0] != 5 || arr[size - 1] != 17) - { - Console.WriteLine("Scenario 2 for GC.AllocUninitialized() failed!"); - return 1; - } - } - - // allocate a string array - { - int i = 100; - var arr = AllocUninitialized.Call(i); - - arr[0] = "5"; - arr[i - 1] = "17"; - if (arr[0] != "5" || arr[i - 1] != "17") - { - Console.WriteLine("Scenario 3 for GC.AllocUninitialized() failed!"); - return 1; - } - } - - // allocate max size byte array - { - if (IntPtr.Size == 8) - { - int i = 0x7FFFFFC7; - var arr = AllocUninitialized.Call(i); - - arr[0] = 5; - arr[i - 1] = 17; - if (arr[0] != 5 || arr[i - 1] != 17) - { - Console.WriteLine("Scenario 4 for GC.AllocUninitialized() failed!"); - return 1; - } - } - } - - // negative size - { - int GetNegativeValue() => -1; - int negativeSize = GetNegativeValue(); - Type expectedExceptionType = null; - - try - { - GC.KeepAlive(new byte[negativeSize]); - - Console.WriteLine("Scenario 5 Expected exception (new operator)!"); - return 1; - } - catch (Exception newOperatorEx) - { - expectedExceptionType = newOperatorEx.GetType(); - } - - try - { - var arr = AllocUninitialized.Call(-1); - - Console.WriteLine("Scenario 5 Expected exception (GC.AllocateUninitializedArray)!"); - return 1; - } - catch (Exception allocUninitializedEx) when (allocUninitializedEx.GetType() == expectedExceptionType) - { - // OK - } - catch (Exception other) - { - Console.WriteLine($"Scenario 5 Expected exception type mismatch: expected {expectedExceptionType}, but got {other.GetType()}!"); - return 1; - } - } - - // too large - { - try - { - var arr = AllocUninitialized.Call(int.MaxValue); - - Console.WriteLine("Scenario 6 Expected exception!"); - return 1; - } - catch (OutOfMemoryException) - { - } - } - - - Console.WriteLine("Test for GC.Collect() passed!"); - return 100; - } - - //TODO: This should be removed once the API is public. - static class AllocUninitialized - { - public static Func Call = (i) => - { - // replace the stub with actual impl. - Call = (Func)typeof(System.GC). - GetMethod("AllocateUninitializedArray", - bindingAttr: System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static, - binder: null, - new Type[] { typeof(int) }, - modifiers: new System.Reflection.ParameterModifier[0]). - MakeGenericMethod(new Type[] { typeof(T) }). - CreateDelegate(typeof(Func)); - - // call the impl. - return Call(i); - }; - } -} diff --git a/src/coreclr/tests/src/GC/API/GC/AllocateUninitializedArray.csproj b/src/coreclr/tests/src/GC/API/GC/AllocateUninitializedArray.csproj deleted file mode 100644 index 41499bc..0000000 --- a/src/coreclr/tests/src/GC/API/GC/AllocateUninitializedArray.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - Exe - 0 - - - - PdbOnly - - - - - diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index da3de89..d09b7e3 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -2029,6 +2029,8 @@ namespace System { public static int MaxGeneration { get { throw null; } } public static void AddMemoryPressure(long bytesAllocated) { } + public static T[] AllocateArray(int length, bool pinned = false) { throw null; } + public static T[] AllocateUninitializedArray(int length, bool pinned = false) { throw null; } public static void CancelFullGCNotification() { } public static void Collect() { } public static void Collect(int generation) { } diff --git a/src/libraries/System.Runtime/tests/System/GCTests.cs b/src/libraries/System.Runtime/tests/System/GCTests.cs index 4f65939..b7e3070 100644 --- a/src/libraries/System.Runtime/tests/System/GCTests.cs +++ b/src/libraries/System.Runtime/tests/System/GCTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Diagnostics; @@ -424,7 +425,8 @@ namespace System.Tests public static void LatencyRoundtrips_LowLatency(GCLatencyMode value) => LatencyRoundtrips(value); } - public class GCExtendedTests { + public class GCExtendedTests + { private const int TimeoutMilliseconds = 10 * 30 * 1000; //if full GC is triggered it may take a while /// @@ -892,5 +894,178 @@ namespace System.Tests previous = CallGetTotalAllocatedBytes(previous); } } + + [Fact] + [OuterLoop] + [ActiveIssue("https://github.com/dotnet/runtime/issues/33583", TestRuntimes.Mono)] + private static void AllocateUninitializedArray() + { + // allocate a bunch of SOH byte arrays and touch them. + var r = new Random(1234); + for (int i = 0; i < 10000; i++) + { + int size = r.Next(10000); + var arr = GC.AllocateUninitializedArray(size, pinned: i % 2 == 1); + + if (size > 1) + { + arr[0] = 5; + arr[size - 1] = 17; + Assert.True(arr[0] == 5 && arr[size - 1] == 17); + } + } + + // allocate a bunch of LOH int arrays and touch them. + for (int i = 0; i < 1000; i++) + { + int size = r.Next(100000, 1000000); + var arr = GC.AllocateUninitializedArray(size, pinned: i % 2 == 1); + + arr[0] = 5; + arr[size - 1] = 17; + Assert.True(arr[0] == 5 && arr[size - 1] == 17); + } + + // allocate a string array + { + int i = 100; + var arr = GC.AllocateUninitializedArray(i); + + arr[0] = "5"; + arr[i - 1] = "17"; + Assert.True(arr[0] == "5" && arr[i - 1] == "17"); + } + + // allocate max size byte array + { + if (IntPtr.Size == 8) + { + int i = 0x7FFFFFC7; + var arr = GC.AllocateUninitializedArray(i); + + arr[0] = 5; + arr[i - 1] = 17; + Assert.True(arr[0] == 5 && arr[i - 1] == 17); + } + } + } + + [Fact] + [OuterLoop] + [ActiveIssue("https://github.com/dotnet/runtime/issues/33583", TestRuntimes.Mono)] + private static void AllocateArray() + { + // allocate a bunch of SOH byte arrays and touch them. + var r = new Random(1234); + for (int i = 0; i < 10000; i++) + { + int size = r.Next(10000); + var arr = GC.AllocateArray(size, pinned: i % 2 == 1); + + if (size > 1) + { + arr[0] = 5; + arr[size - 1] = 17; + Assert.True(arr[0] == 5 && arr[size - 1] == 17); + } + } + + // allocate a bunch of LOH int arrays and touch them. + for (int i = 0; i < 1000; i++) + { + int size = r.Next(100000, 1000000); + var arr = GC.AllocateArray(size, pinned: i % 2 == 1); + + arr[0] = 5; + arr[size - 1] = 17; + Assert.True(arr[0] == 5 && arr[size - 1] == 17); + } + + // allocate a string array + { + int i = 100; + var arr = GC.AllocateArray(i); + + arr[0] = "5"; + arr[i - 1] = "17"; + Assert.True(arr[0] == "5" && arr[i - 1] == "17"); + } + + // allocate max size byte array + { + if (IntPtr.Size == 8) + { + int i = 0x7FFFFFC7; + var arr = GC.AllocateArray(i); + + arr[0] = 5; + arr[i - 1] = 17; + Assert.True(arr[0] == 5 && arr[i - 1] == 17); + } + } + } + + [Theory] + [InlineData(-1)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/33583", TestRuntimes.Mono)] + private static void AllocateArrayNegativeSize(int negValue) + { + Assert.Throws(() => GC.AllocateUninitializedArray(-1)); + Assert.Throws(() => GC.AllocateUninitializedArray(negValue)); + Assert.Throws(() => GC.AllocateUninitializedArray(-1, pinned: true)); + Assert.Throws(() => GC.AllocateUninitializedArray(negValue, pinned: true)); + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/33583", TestRuntimes.Mono)] + private static void AllocateArrayTooLarge() + { + Assert.Throws(() => GC.AllocateUninitializedArray(int.MaxValue)); + Assert.Throws(() => GC.AllocateUninitializedArray(int.MaxValue, pinned: true)); + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/33583", TestRuntimes.Mono)] + private static void AllocateArrayRefType() + { + GC.AllocateUninitializedArray(100); + Assert.Throws(() => GC.AllocateUninitializedArray(100, pinned: true)); + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/33583", TestRuntimes.Mono)] + private unsafe static void AllocateArrayCheckPinning() + { + var list = new List(); + + var r = new Random(1234); + for (int i = 0; i < 10000; i++) + { + int size = r.Next(2, 100); + var arr = i % 2 == 1 ? + GC.AllocateArray(size, pinned: true) : + GC.AllocateUninitializedArray(size, pinned: true) ; + + fixed (long* pElem = &arr[0]) + { + *pElem = (long)pElem; + } + + if (r.Next(100) % 2 == 0) + { + list.Add(arr); + } + } + + GC.Collect(); + + foreach (var arr in list) + { + fixed (long* pElem = &arr[0]) + { + Assert.Equal(*pElem, (long)pElem); + } + } + } } } diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/GC.Mono.cs b/src/mono/netcore/System.Private.CoreLib/src/System/GC.Mono.cs index e5efe52..5894af3 100644 --- a/src/mono/netcore/System.Private.CoreLib/src/System/GC.Mono.cs +++ b/src/mono/netcore/System.Private.CoreLib/src/System/GC.Mono.cs @@ -246,17 +246,20 @@ namespace System public static GCMemoryInfo GetGCMemoryInfo () { - _GetGCMemoryInfo(out long highMemoryLoadThresholdBytes, + _GetGCMemoryInfo (out long highMemoryLoadThresholdBytes, out long memoryLoadBytes, out long totalAvailableMemoryBytes, out long heapSizeBytes, out long fragmentedBytes ); - return new GCMemoryInfo(highMemoryLoadThresholdBytes, memoryLoadBytes, totalAvailableMemoryBytes, heapSizeBytes, fragmentedBytes); + return new GCMemoryInfo (highMemoryLoadThresholdBytes, memoryLoadBytes, totalAvailableMemoryBytes, heapSizeBytes, fragmentedBytes); } - internal static T[] AllocateUninitializedArray (int length) + public static T[] AllocateUninitializedArray (int length, bool pinned = false) { + if (pinned) + throw new NotImplementedException (); + // Mono only does explicit zeroning if the array is to big for the nursery, but less than 1 Mb - 4 kb. // If it is bigger than that, we grab memoroy directly from the OS which comes pre-zeroed. // Experimentation shows that if we just skip the zeroing in this case, we do not save a measurable @@ -264,5 +267,13 @@ namespace System // Revist if we change LOS implementation. return new T [length]; } + + public static T[] AllocateArray (int length, bool pinned = false) + { + if (pinned) + throw new NotImplementedException (); + + return new T [length]; + } } } -- 2.7.4