diff --git a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs index 184ce80f44b899..0c88b7ada8e0c4 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs @@ -60,7 +60,7 @@ public enum GCNotificationStatus public static partial class GC { [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void GetMemoryInfo(GCMemoryInfoData data, int kind); + private static extern unsafe void GetMemoryInfo(GCMemoryInfoData* data, int kind); /// Gets garbage collection memory information. /// An object that contains information about the garbage collector's memory usage. @@ -69,7 +69,7 @@ public static partial class GC /// Gets garbage collection memory information. /// The kind of collection for which to retrieve memory information. /// An object that contains information about the garbage collector's memory usage. - public static GCMemoryInfo GetGCMemoryInfo(GCKind kind) + public static unsafe GCMemoryInfo GetGCMemoryInfo(GCKind kind) { if ((kind < GCKind.Any) || (kind > GCKind.Background)) { @@ -80,8 +80,8 @@ public static GCMemoryInfo GetGCMemoryInfo(GCKind kind) GCKind.Background)); } - var data = new GCMemoryInfoData(); - GetMemoryInfo(data, (int)kind); + GCMemoryInfoData data = default; + GetMemoryInfo(&data, (int)kind); return new GCMemoryInfo(data); } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs index 67d09f8b246a6d..3905b7f3e74b2d 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs @@ -764,7 +764,7 @@ public static long GetTotalAllocatedBytes(bool precise = false) /// Gets garbage collection memory information. /// The kind of collection for which to retrieve memory information. /// An object that contains information about the garbage collector's memory usage. - public static GCMemoryInfo GetGCMemoryInfo(GCKind kind) + public static unsafe GCMemoryInfo GetGCMemoryInfo(GCKind kind) { if ((kind < GCKind.Any) || (kind > GCKind.Background)) { @@ -775,8 +775,8 @@ public static GCMemoryInfo GetGCMemoryInfo(GCKind kind) GCKind.Background)); } - var data = new GCMemoryInfoData(); - RuntimeImports.RhGetMemoryInfo(ref data.GetRawData(), kind); + GCMemoryInfoData data = default; + RuntimeImports.RhGetMemoryInfo(&data, kind); return new GCMemoryInfo(data); } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index 4fe7ee2e706106..bf140a03c69a75 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -241,7 +241,7 @@ internal struct GCHeapHardLimitInfo [MethodImpl(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhGetMemoryInfo")] - internal static extern void RhGetMemoryInfo(ref byte info, GCKind kind); + internal static extern unsafe void RhGetMemoryInfo(GCMemoryInfoData* data, GCKind kind); [LibraryImport(RuntimeLibrary)] internal static unsafe partial void RhAllocateNewArray(MethodTable* pArrayEEType, uint numElements, uint flags, void* pResult); diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index d039226c328cad..031cb04bff2d9f 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -584,32 +584,30 @@ FCIMPL0(INT64, GCInterface::GetTotalPauseDuration) } FCIMPLEND -FCIMPL2(void, GCInterface::GetMemoryInfo, Object* objUNSAFE, int kind) +FCIMPL2(void, GCInterface::GetMemoryInfo, GCMemoryInfoData* pData, int kind) { FCALL_CONTRACT; FC_GC_POLL_NOT_NEEDED(); - GCMEMORYINFODATAREF objGCMemoryInfo = (GCMEMORYINFODATAREF)(ObjectToOBJECTREF (objUNSAFE)); - - UINT64* genInfoRaw = (UINT64*)&(objGCMemoryInfo->generationInfo0); - UINT64* pauseInfoRaw = (UINT64*)&(objGCMemoryInfo->pauseDuration0); + UINT64* genInfoRaw = (UINT64*)&(pData->generationInfo0); + UINT64* pauseInfoRaw = (UINT64*)&(pData->pauseDuration0); return GCHeapUtilities::GetGCHeap()->GetMemoryInfo( - &(objGCMemoryInfo->highMemLoadThresholdBytes), - &(objGCMemoryInfo->totalAvailableMemoryBytes), - &(objGCMemoryInfo->lastRecordedMemLoadBytes), - &(objGCMemoryInfo->lastRecordedHeapSizeBytes), - &(objGCMemoryInfo->lastRecordedFragmentationBytes), - &(objGCMemoryInfo->totalCommittedBytes), - &(objGCMemoryInfo->promotedBytes), - &(objGCMemoryInfo->pinnedObjectCount), - &(objGCMemoryInfo->finalizationPendingCount), - &(objGCMemoryInfo->index), - &(objGCMemoryInfo->generation), - &(objGCMemoryInfo->pauseTimePercent), - (bool*)&(objGCMemoryInfo->isCompaction), - (bool*)&(objGCMemoryInfo->isConcurrent), + &(pData->highMemLoadThresholdBytes), + &(pData->totalAvailableMemoryBytes), + &(pData->lastRecordedMemLoadBytes), + &(pData->lastRecordedHeapSizeBytes), + &(pData->lastRecordedFragmentationBytes), + &(pData->totalCommittedBytes), + &(pData->promotedBytes), + &(pData->pinnedObjectCount), + &(pData->finalizationPendingCount), + &(pData->index), + &(pData->generation), + &(pData->pauseTimePercent), + (bool*)&(pData->isCompaction), + (bool*)&(pData->isConcurrent), genInfoRaw, pauseInfoRaw, kind); diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h index 1f5fda9ed02ccc..cf8cc472622dae 100644 --- a/src/coreclr/vm/comutilnative.h +++ b/src/coreclr/vm/comutilnative.h @@ -101,7 +101,7 @@ struct GCGenerationInfo }; #include "pshpack4.h" -class GCMemoryInfoData : public Object +struct GCMemoryInfoData { public: UINT64 highMemLoadThresholdBytes; @@ -131,14 +131,6 @@ class GCMemoryInfoData : public Object }; #include "poppack.h" -#ifdef USE_CHECKED_OBJECTREFS -typedef REF GCMEMORYINFODATA; -typedef REF GCMEMORYINFODATAREF; -#else // USE_CHECKED_OBJECTREFS -typedef GCMemoryInfoData * GCMEMORYINFODATA; -typedef GCMemoryInfoData * GCMEMORYINFODATAREF; -#endif // USE_CHECKED_OBJECTREFS - using EnumerateConfigurationValuesCallback = void (*)(void* context, void* name, void* publicKey, GCConfigurationType type, int64_t data); struct GCHeapHardLimitInfo @@ -166,7 +158,7 @@ class GCInterface { static FORCEINLINE UINT64 InterlockedSub(UINT64 *pMinuend, UINT64 subtrahend); static FCDECL0(INT64, GetTotalPauseDuration); - static FCDECL2(void, GetMemoryInfo, Object* objUNSAFE, int kind); + static FCDECL2(void, GetMemoryInfo, GCMemoryInfoData* pData, int kind); static FCDECL0(UINT32, GetMemoryLoad); static FCDECL0(int, GetGcLatencyMode); static FCDECL1(int, SetGcLatencyMode, int newLatencyMode); diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/SharedArrayPool.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/SharedArrayPool.cs index 89f32fe337f161..7a91aef50cf3f5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/SharedArrayPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/SharedArrayPool.cs @@ -209,14 +209,14 @@ public bool Trim() { if (!log.IsEnabled()) { - foreach (KeyValuePair tlsBuckets in _allTlsBuckets) + foreach (KeyValuePair tlsBuckets in _allTlsBuckets.EnumerateOnStack()) { Array.Clear(tlsBuckets.Key); } } else { - foreach (KeyValuePair tlsBuckets in _allTlsBuckets) + foreach (KeyValuePair tlsBuckets in _allTlsBuckets.EnumerateOnStack()) { SharedArrayPoolThreadLocalArray[] buckets = tlsBuckets.Key; for (int i = 0; i < buckets.Length; i++) @@ -241,7 +241,7 @@ public bool Trim() _ => 30_000, }; - foreach (KeyValuePair tlsBuckets in _allTlsBuckets) + foreach (KeyValuePair tlsBuckets in _allTlsBuckets.EnumerateOnStack()) { SharedArrayPoolThreadLocalArray[] buckets = tlsBuckets.Key; for (int i = 0; i < buckets.Length; i++) diff --git a/src/libraries/System.Private.CoreLib/src/System/GCMemoryInfo.cs b/src/libraries/System.Private.CoreLib/src/System/GCMemoryInfo.cs index 59cce34d7d4038..0b1eba089cd1ca 100644 --- a/src/libraries/System.Private.CoreLib/src/System/GCMemoryInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/GCMemoryInfo.cs @@ -54,7 +54,7 @@ public enum GCKind }; [StructLayout(LayoutKind.Sequential)] - internal sealed class GCMemoryInfoData + internal struct GCMemoryInfoData { internal long _highMemoryLoadThresholdBytes; internal long _totalAvailableMemoryBytes; diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs index eb6af90b5a5043..1dfd43f9c01e5a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs @@ -369,8 +369,8 @@ IEnumerator> IEnumerable>. IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable>)this).GetEnumerator(); - /// Provides an enumerator for the table. - private sealed class Enumerator : IEnumerator> + /// The core implementation for the enumerators for the table. + private struct EnumeratorCore : IDisposable { // The enumerator would ideally hold a reference to the Container and the end index within that // container. However, the safety of the CWT depends on the only reference to the Container being @@ -393,7 +393,7 @@ private sealed class Enumerator : IEnumerator> private int _currentIndex; // the current index into the container private KeyValuePair _current; // the current entry set by MoveNext and returned from Current - public Enumerator(ConditionalWeakTable table) + public EnumeratorCore(ConditionalWeakTable table) { Debug.Assert(table != null, "Must provide a valid table"); Debug.Assert(Monitor.IsEntered(table._lock), "Must hold the _lock lock to construct the enumerator"); @@ -410,11 +410,6 @@ public Enumerator(ConditionalWeakTable table) _currentIndex = -1; } - ~Enumerator() - { - Dispose(); - } - public void Dispose() { // Use an interlocked operation to ensure that only one thread can get access to @@ -485,6 +480,56 @@ public KeyValuePair Current return _current; } } + } + + /// Provides an enumerator for the table. + private sealed class Enumerator : IEnumerator> + { + private EnumeratorCore _enumeratorCore; + + public Enumerator(ConditionalWeakTable table) => _enumeratorCore = new(table); + + ~Enumerator() + { + Dispose(); + } + + public void Dispose() => _enumeratorCore.Dispose(); + + public bool MoveNext() => _enumeratorCore.MoveNext(); + + public KeyValuePair Current => _enumeratorCore.Current; + + object? IEnumerator.Current => Current; + + public void Reset() { } + } + + /// Gets an enumerable used to enumerate the values on the stack without allocation. + // This API is unsafe to use if the user neglects to call Dispose, which is why + // we can only use this internally where we can enforce proper foreach usage. + // It's also unsafe to use in async functions, even with proper foreach, because there's + // a chance that the async will never continue, in which case we need to rely on the finalizer in the regular enumerator. + internal RefEnumerable EnumerateOnStack() => new RefEnumerable(this); + + /// Provides an enumerable used to enumerate the values on the stack without allocation. + internal readonly ref struct RefEnumerable(ConditionalWeakTable table) + { + public RefEnumerator GetEnumerator() => new(table); + } + + /// Provides an enumerator for the table that can be used on the stack without allocation. + internal ref struct RefEnumerator : IEnumerator> + { + private EnumeratorCore _enumeratorCore; + + public RefEnumerator(ConditionalWeakTable table) => _enumeratorCore = new(table); + + public void Dispose() => _enumeratorCore.Dispose(); + + public bool MoveNext() => _enumeratorCore.MoveNext(); + + public KeyValuePair Current => _enumeratorCore.Current; object? IEnumerator.Current => Current; diff --git a/src/mono/System.Private.CoreLib/src/System/GC.Mono.cs b/src/mono/System.Private.CoreLib/src/System/GC.Mono.cs index 21eba8223fd0f9..ce69362d8a397d 100644 --- a/src/mono/System.Private.CoreLib/src/System/GC.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/GC.Mono.cs @@ -248,7 +248,7 @@ private static extern void _GetGCMemoryInfo(out long highMemoryLoadThresholdByte public static GCMemoryInfo GetGCMemoryInfo() { - var data = new GCMemoryInfoData(); + GCMemoryInfoData data = default; _GetGCMemoryInfo(out data._highMemoryLoadThresholdBytes, out data._memoryLoadBytes,