* 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
<!-- Methods are used to register and unregister frozen segments. They are private and experimental. -->
<method name="_RegisterFrozenSegment" />
<method name="_UnregisterFrozenSegment" />
- <!-- This is an internal API for now and is not yet used outside tests. -->
- <method name="AllocateUninitializedArray" />
</type>
<!-- Properties and methods used by a debugger. -->
<type fullname="System.Threading.Tasks.Task">
[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);
}
/// <summary>
- /// 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.
/// </summary>
+ /// <typeparam name="T">Specifies the type of the array element.</typeparam>
+ /// <param name="length">Specifies the length of the array.</param>
+ /// <param name="pinned">Specifies whether the allocated array must be pinned.</param>
+ /// <remarks>
+ /// If pinned is set to true, <typeparamref name="T"/> must not be a reference type or a type that contains object references.
+ /// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)] // forced to ensure no perf drop for small memory buffers (hot path)
- internal static T[] AllocateUninitializedArray<T>(int length)
+ public static T[] AllocateUninitializedArray<T>(int length, bool pinned = false)
{
- if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+ if (!pinned)
{
- return new T[length];
- }
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+ {
+ 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<T>())
+ // small arrays are allocated using `new[]` as that is generally faster.
+ if (length < 2048 / Unsafe.SizeOf<T>())
+ {
+ return new T[length];
+ }
+#endif
+ }
+ else if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
- 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<T[]>(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<T[]>(AllocateNewArray(typeof(T[]).TypeHandle.Value, length, flags));
+ }
+ }
+
+ /// <summary>
+ /// Allocate an array.
+ /// </summary>
+ /// <typeparam name="T">Specifies the type of the array element.</typeparam>
+ /// <param name="length">Specifies the length of the array.</param>
+ /// <param name="pinned">Specifies whether the allocated array must be pinned.</param>
+ /// <remarks>
+ /// If pinned is set to true, <typeparamref name="T"/> must not be a reference type or a type that contains object references.
+ /// </remarks>
+ public static T[] AllocateArray<T>(int length, bool pinned = false)
+ {
+ GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS.GC_ALLOC_NO_FLAGS;
+
+ if (pinned)
+ {
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+ ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
+
+ flags = GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP;
+ }
+
+ return Unsafe.As<T[]>(AllocateNewArray(typeof(T[]).TypeHandle.Value, length, flags));
}
}
}
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)
#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
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
#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,
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)
** 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;
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();
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
//
//========================================================================
-#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;
// 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
{
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",
#endif
}
#else
-#define LogAlloc(size, pMT, object)
+#define LogAlloc( object)
#endif
+// signals completion of the object to GC and sends events if necessary
+template <class TObj>
+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)
{
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;
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;
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());
if (elemType == ELEMENT_TYPE_VOID)
COMPlusThrow(kArgumentException);
- // IBC Log MethodTable access
- g_IBCLogger.LogMethodTableAccess(pArrayMT);
-
if (cElements < 0)
COMPlusThrow(kOverflowException);
((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
{
#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()
//
// 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
{
MethodTable* pArrayMT = arrayType.AsMethodTable();
- return AllocateArrayEx(pArrayMT, pArgs, dwNumArgs, flags, bAllocateInLargeHeap);
+ return AllocateArrayEx(pArrayMT, pArgs, dwNumArgs, flags);
}
//
// allocate sub-arrays and fill them in.
//
// For arrays with lower bounds, pBounds is <lower bound 1>, <count 1>, <lower bound 2>, ...
-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;
PRECONDITION(dwNumArgs > 0);
} CONTRACTL_END;
- ArrayBase * orArray = NULL;
-
#ifdef _DEBUG
if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP))
{
}
#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();
// 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;
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);
(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
{
}
#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();
}
}
- // 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)
{
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);
}
_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 )
MODE_COOPERATIVE; // returns an objref without pinning it => cooperative
} CONTRACTL_END;
- StringObject *orObject = NULL;
-
#ifdef _DEBUG
if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP))
{
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>@TODO need to build a LARGE g_pStringMethodTable before</TODO>
- 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
MODE_COOPERATIVE; // returns an objref without pinning it => cooperative
} CONTRACTL_END;
- Utf8StringObject *orObject = NULL;
-
#ifdef _DEBUG
if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP))
{
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
#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())
_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);
}
#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
//========================================================================
// 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);
// 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);
_ASSERTE(allocPtr != nullptr);
ArrayBase *array = reinterpret_cast<ArrayBase *>(allocPtr);
- array->SetArrayMethodTable(pArrayMT);
+ array->SetMethodTable(pArrayMT);
_ASSERTE(static_cast<DWORD>(componentCount) == componentCount);
array->m_NumComponents = static_cast<DWORD>(componentCount);
_ASSERTE(allocPtr != nullptr);
ArrayBase *array = reinterpret_cast<ArrayBase *>(allocPtr);
- array->SetArrayMethodTable(pArrayMT);
+ array->SetMethodTable(pArrayMT);
_ASSERTE(static_cast<DWORD>(componentCount) == componentCount);
array->m_NumComponents = static_cast<DWORD>(componentCount);
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
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
};
/*
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;
// 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;
{
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;
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;
<ExcludeList Include="$(XunitTestBinBase)/GC/API/GC/AddUsageTest/**">
<Issue>needs triage</Issue>
</ExcludeList>
- <ExcludeList Include="$(XunitTestBinBase)/GC/API/GC/AllocateUninitializedArray/**">
- <Issue>needs triage</Issue>
- </ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/GC/API/GC/Collect1/**">
<Issue>needs triage</Issue>
</ExcludeList>
+++ /dev/null
-// 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<byte>.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<int>.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<string>.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<byte>.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<byte>.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<double>.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<T>
- {
- public static Func<int, T[]> Call = (i) =>
- {
- // replace the stub with actual impl.
- Call = (Func<int, T[]>)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<int, T[]>));
-
- // call the impl.
- return Call(i);
- };
- }
-}
+++ /dev/null
-<Project Sdk="Microsoft.NET.Sdk">
- <PropertyGroup>
- <OutputType>Exe</OutputType>
- <CLRTestPriority>0</CLRTestPriority>
- </PropertyGroup>
- <PropertyGroup>
- <!-- Set to 'Full' if the Debug? column is marked in the spreadsheet. Leave blank otherwise. -->
- <DebugType>PdbOnly</DebugType>
- </PropertyGroup>
- <ItemGroup>
- <Compile Include="AllocateUninitializedArray.cs" />
- </ItemGroup>
-</Project>
{
public static int MaxGeneration { get { throw null; } }
public static void AddMemoryPressure(long bytesAllocated) { }
+ public static T[] AllocateArray<T>(int length, bool pinned = false) { throw null; }
+ public static T[] AllocateUninitializedArray<T>(int length, bool pinned = false) { throw null; }
public static void CancelFullGCNotification() { }
public static void Collect() { }
public static void Collect(int generation) { }
// 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;
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
/// <summary>
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<byte>(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<int>(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<string>(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<byte>(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<byte>(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<int>(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<string>(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<byte>(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<OverflowException>(() => GC.AllocateUninitializedArray<byte>(-1));
+ Assert.Throws<OverflowException>(() => GC.AllocateUninitializedArray<byte>(negValue));
+ Assert.Throws<OverflowException>(() => GC.AllocateUninitializedArray<byte>(-1, pinned: true));
+ Assert.Throws<OverflowException>(() => GC.AllocateUninitializedArray<byte>(negValue, pinned: true));
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/33583", TestRuntimes.Mono)]
+ private static void AllocateArrayTooLarge()
+ {
+ Assert.Throws<OutOfMemoryException>(() => GC.AllocateUninitializedArray<double>(int.MaxValue));
+ Assert.Throws<OutOfMemoryException>(() => GC.AllocateUninitializedArray<double>(int.MaxValue, pinned: true));
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/33583", TestRuntimes.Mono)]
+ private static void AllocateArrayRefType()
+ {
+ GC.AllocateUninitializedArray<string>(100);
+ Assert.Throws<ArgumentException>(() => GC.AllocateUninitializedArray<string>(100, pinned: true));
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/33583", TestRuntimes.Mono)]
+ private unsafe static void AllocateArrayCheckPinning()
+ {
+ var list = new List<long[]>();
+
+ 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<long>(size, pinned: true) :
+ GC.AllocateUninitializedArray<long>(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);
+ }
+ }
+ }
}
}
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<T> (int length)
+ public static T[] AllocateUninitializedArray<T> (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
// Revist if we change LOS implementation.
return new T [length];
}
+
+ public static T[] AllocateArray<T> (int length, bool pinned = false)
+ {
+ if (pinned)
+ throw new NotImplementedException ();
+
+ return new T [length];
+ }
}
}