Skip to content

Commit 5b09cbf

Browse files
authored
Make the RefreshMemoryLimit API public (#85549)
1 parent d7a30ba commit 5b09cbf

File tree

15 files changed

+194
-24
lines changed

15 files changed

+194
-24
lines changed

src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,42 @@ internal enum GCConfigurationType
783783
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCInterface_EnumerateConfigurationValues")]
784784
internal static unsafe partial void _EnumerateConfigurationValues(void* configurationDictionary, delegate* unmanaged<void*, void*, void*, GCConfigurationType, long, void> callback);
785785

786-
private static int _RefreshMemoryLimit()
786+
internal enum RefreshMemoryStatus
787+
{
788+
Succeeded = 0,
789+
HardLimitTooLow = 1,
790+
HardLimitInvalid = 2,
791+
}
792+
793+
/// <summary>
794+
///
795+
/// Instructs the Garbage Collector to reconfigure itself by detecting the various memory limits on the system.
796+
///
797+
/// In addition to actual physical memory limit and container limit settings, these configuration settings can be overwritten:
798+
///
799+
/// - GCHeapHardLimit
800+
/// - GCHeapHardLimitPercent
801+
/// - GCHeapHardLimitSOH
802+
/// - GCHeapHardLimitLOH
803+
/// - GCHeapHardLimitPOH
804+
/// - GCHeapHardLimitSOHPercent
805+
/// - GCHeapHardLimitLOHPercent
806+
/// - GCHeapHardLimitPOHPercent
807+
///
808+
/// Instead of updating the environment variable (which will not be read), these are overridden setting a ulong value in the AppContext.
809+
///
810+
/// For example, you can use AppContext.SetData("GCHeapHardLimit", (ulong) 100 * 1024 * 1024) to override the GCHeapHardLimit to a 100M.
811+
///
812+
/// This API will only handle configs that could be handled when the runtime is loaded, for example, for configs that don't have any effects on 32-bit systems (like the GCHeapHardLimit* ones), this API will not handle it.
813+
///
814+
/// As of now, this API is feature preview only and subject to changes as necessary.
815+
///
816+
/// <exception cref="InvalidOperationException">If the hard limit is too low. This can happen if the heap hard limit that the refresh will set, either because of new AppData settings or implied by the container memory limit changes, is lower than what is already committed.</exception>"
817+
/// <exception cref="InvalidOperationException">If the hard limit is invalid. This can happen, for example, with negative heap hard limit percentages.</exception>"
818+
///
819+
/// </summary>
820+
[System.Runtime.Versioning.RequiresPreviewFeaturesAttribute("RefreshMemoryLimit is in preview.")]
821+
public static void RefreshMemoryLimit()
787822
{
788823
ulong heapHardLimit = (AppContext.GetData("GCHeapHardLimit") as ulong?) ?? ulong.MaxValue;
789824
ulong heapHardLimitPercent = (AppContext.GetData("GCHeapHardLimitPercent") as ulong?) ?? ulong.MaxValue;
@@ -804,11 +839,19 @@ private static int _RefreshMemoryLimit()
804839
HeapHardLimitLOHPercent = heapHardLimitLOHPercent,
805840
HeapHardLimitPOHPercent = heapHardLimitPOHPercent,
806841
};
807-
return RefreshMemoryLimit(heapHardLimitInfo);
842+
RefreshMemoryStatus status = (RefreshMemoryStatus)_RefreshMemoryLimit(heapHardLimitInfo);
843+
switch (status)
844+
{
845+
case RefreshMemoryStatus.HardLimitTooLow:
846+
throw new InvalidOperationException(SR.InvalidOperationException_HardLimitTooLow);
847+
case RefreshMemoryStatus.HardLimitInvalid:
848+
throw new InvalidOperationException(SR.InvalidOperationException_HardLimitInvalid);
849+
}
850+
Debug.Assert(status == RefreshMemoryStatus.Succeeded);
808851
}
809852

810853
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCInterface_RefreshMemoryLimit")]
811-
internal static partial int RefreshMemoryLimit(GCHeapHardLimitInfo heapHardLimitInfo);
854+
internal static partial int _RefreshMemoryLimit(GCHeapHardLimitInfo heapHardLimitInfo);
812855

813856
internal struct GCHeapHardLimitInfo
814857
{

src/coreclr/gc/gc.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49788,11 +49788,11 @@ bool gc_heap::compute_memory_settings(bool is_initialization, uint32_t& nhp, uin
4978849788

4978949789
int gc_heap::refresh_memory_limit()
4979049790
{
49791-
int status = REFRESH_MEMORY_SUCCEED;
49791+
refresh_memory_limit_status status = refresh_success;
4979249792

4979349793
if (GCConfig::GetGCTotalPhysicalMemory() != 0)
4979449794
{
49795-
return status;
49795+
return (int)status;
4979649796
}
4979749797

4979849798
GCToEEInterface::SuspendEE(SUSPEND_FOR_GC);
@@ -49927,7 +49927,7 @@ int gc_heap::refresh_memory_limit()
4992749927
if (!compute_hard_limit())
4992849928
{
4992949929
succeed = false;
49930-
status = REFRESH_MEMORY_HARD_LIMIT_INVALID;
49930+
status = refresh_hard_limit_invalid;
4993149931
}
4993249932
hard_limit_config_p = heap_hard_limit != 0;
4993349933
#else
@@ -49937,7 +49937,7 @@ int gc_heap::refresh_memory_limit()
4993749937
if (succeed && !compute_memory_settings(false, nhp, nhp_from_config, seg_size_from_config, new_current_total_committed))
4993849938
{
4993949939
succeed = false;
49940-
status = REFRESH_MEMORY_HARD_LIMIT_TOO_LOW;
49940+
status = refresh_hard_limit_too_low;
4994149941
}
4994249942

4994349943
if (!succeed)
@@ -49969,7 +49969,7 @@ int gc_heap::refresh_memory_limit()
4996949969
#ifdef COMMITTED_BYTES_SHADOW
4997049970
assert (new_committed_by_oh[i] == committed_by_oh[i]);
4997149971
#else
49972-
new_committed_by_oh[i] = committed_by_oh[i];
49972+
committed_by_oh[i] = new_committed_by_oh[i];
4997349973
#endif
4997449974
}
4997549975
#ifdef MULTIPLE_HEAPS
@@ -49998,7 +49998,7 @@ int gc_heap::refresh_memory_limit()
4999849998
#endif
4999949999
GCToEEInterface::RestartEE(TRUE);
5000050000

50001-
return status;
50001+
return (int)status;
5000250002
}
5000350003

5000450004
#ifdef USE_REGIONS

src/coreclr/gc/gcinterface.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,16 @@ enum end_no_gc_region_status
326326
end_no_gc_alloc_exceeded = 3
327327
};
328328

329+
// !!!!!!!!!!!!!!!!!!!!!!!
330+
// make sure you change the def in bcl\system\gc.cs
331+
// if you change this!
332+
enum refresh_memory_limit_status
333+
{
334+
refresh_success = 0,
335+
refresh_hard_limit_too_low = 1,
336+
refresh_hard_limit_invalid = 2
337+
};
338+
329339
enum gc_kind
330340
{
331341
gc_kind_any = 0, // any of the following kind
@@ -570,10 +580,6 @@ enum class GCConfigurationType
570580

571581
using ConfigurationValueFunc = void (*)(void* context, void* name, void* publicKey, GCConfigurationType type, int64_t data);
572582

573-
const int REFRESH_MEMORY_SUCCEED = 0;
574-
const int REFRESH_MEMORY_HARD_LIMIT_TOO_LOW = 1;
575-
const int REFRESH_MEMORY_HARD_LIMIT_INVALID = 2;
576-
577583
// IGCHeap is the interface that the VM will use when interacting with the GC.
578584
class IGCHeap {
579585
public:

src/coreclr/nativeaot/Runtime/GCHelpers.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,17 @@ EXTERN_C NATIVEAOT_API void __cdecl RhEnumerateConfigurationValues(void* configu
313313
pHeap->EnumerateConfigurationValues(configurationContext, callback);
314314
}
315315

316+
GCHeapHardLimitInfo g_gcHeapHardLimitInfo;
317+
bool g_gcHeapHardLimitInfoSpecified = false;
318+
319+
EXTERN_C NATIVEAOT_API void __cdecl RhRefreshMemoryLimit(GCHeapHardLimitInfo heapHardLimitInfo)
320+
{
321+
IGCHeap* pHeap = GCHeapUtilities::GetGCHeap();
322+
g_gcHeapHardLimitInfo = heapHardLimitInfo;
323+
g_gcHeapHardLimitInfoSpecified = true;
324+
pHeap->RefreshMemoryLimit();
325+
}
326+
316327
EXTERN_C NATIVEAOT_API int64_t __cdecl RhGetTotalAllocatedBytesPrecise()
317328
{
318329
int64_t allocated;

src/coreclr/nativeaot/Runtime/gcenv.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,18 @@ typedef DPTR(uint32_t) PTR_uint32_t;
9797

9898
enum CLRDataEnumMemoryFlags : int;
9999

100+
struct GCHeapHardLimitInfo
101+
{
102+
uint64_t heapHardLimit;
103+
uint64_t heapHardLimitPercent;
104+
uint64_t heapHardLimitSOH;
105+
uint64_t heapHardLimitLOH;
106+
uint64_t heapHardLimitPOH;
107+
uint64_t heapHardLimitSOHPercent;
108+
uint64_t heapHardLimitLOHPercent;
109+
uint64_t heapHardLimitPOHPercent;
110+
};
111+
100112
/* _TRUNCATE */
101113
#if !defined (_TRUNCATE)
102114
#define _TRUNCATE ((size_t)-1)

src/coreclr/nativeaot/Runtime/gcrhenv.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,8 +1374,31 @@ bool GCToEEInterface::GetBooleanConfigValue(const char* privateKey, const char*
13741374
return true;
13751375
}
13761376

1377+
extern GCHeapHardLimitInfo g_gcHeapHardLimitInfo;
1378+
extern bool g_gcHeapHardLimitInfoSpecified;
1379+
13771380
bool GCToEEInterface::GetIntConfigValue(const char* privateKey, const char* publicKey, int64_t* value)
13781381
{
1382+
#ifdef UNICODE
1383+
size_t keyLength = strlen(privateKey) + 1;
1384+
TCHAR* pKey = (TCHAR*)_alloca(sizeof(TCHAR) * keyLength);
1385+
for (size_t i = 0; i < keyLength; i++)
1386+
pKey[i] = privateKey[i];
1387+
#else
1388+
const TCHAR* pKey = privateKey;
1389+
#endif
1390+
if (g_gcHeapHardLimitInfoSpecified)
1391+
{
1392+
if ((g_gcHeapHardLimitInfo.heapHardLimit != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimit") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimit; return true; }
1393+
if ((g_gcHeapHardLimitInfo.heapHardLimitPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitPercent; return true; }
1394+
if ((g_gcHeapHardLimitInfo.heapHardLimitSOH != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitSOH") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitSOH; return true; }
1395+
if ((g_gcHeapHardLimitInfo.heapHardLimitLOH != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitLOH") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitLOH; return true; }
1396+
if ((g_gcHeapHardLimitInfo.heapHardLimitPOH != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitPOH") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitPOH; return true; }
1397+
if ((g_gcHeapHardLimitInfo.heapHardLimitSOHPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitSOHPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitSOHPercent; return true; }
1398+
if ((g_gcHeapHardLimitInfo.heapHardLimitLOHPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitLOHPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitLOHPercent; return true; }
1399+
if ((g_gcHeapHardLimitInfo.heapHardLimitPOHPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitPOHPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitPOHPercent; return true; }
1400+
}
1401+
13791402
uint64_t uiValue;
13801403
if (!g_pRhConfig->ReadConfigValue(privateKey, &uiValue))
13811404
return false;

src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ internal enum EndNoGCRegionStatus
6161
AllocationExceeded = 3
6262
}
6363

64+
internal enum RefreshMemoryStatus
65+
{
66+
Succeeded = 0,
67+
HardLimitTooLow = 1,
68+
HardLimitInvalid = 2,
69+
}
70+
6471
public static partial class GC
6572
{
6673
public static int GetGeneration(object obj)
@@ -803,5 +810,38 @@ public static TimeSpan GetTotalPauseDuration()
803810
{
804811
return new TimeSpan(RuntimeImports.RhGetTotalPauseDuration());
805812
}
813+
814+
[System.Runtime.Versioning.RequiresPreviewFeaturesAttribute("RefreshMemoryLimit is in preview.")]
815+
public static void RefreshMemoryLimit()
816+
{
817+
ulong heapHardLimit = (AppContext.GetData("GCHeapHardLimit") as ulong?) ?? ulong.MaxValue;
818+
ulong heapHardLimitPercent = (AppContext.GetData("GCHeapHardLimitPercent") as ulong?) ?? ulong.MaxValue;
819+
ulong heapHardLimitSOH = (AppContext.GetData("GCHeapHardLimitSOH") as ulong?) ?? ulong.MaxValue;
820+
ulong heapHardLimitLOH = (AppContext.GetData("GCHeapHardLimitLOH") as ulong?) ?? ulong.MaxValue;
821+
ulong heapHardLimitPOH = (AppContext.GetData("GCHeapHardLimitPOH") as ulong?) ?? ulong.MaxValue;
822+
ulong heapHardLimitSOHPercent = (AppContext.GetData("GCHeapHardLimitSOHPercent") as ulong?) ?? ulong.MaxValue;
823+
ulong heapHardLimitLOHPercent = (AppContext.GetData("GCHeapHardLimitLOHPercent") as ulong?) ?? ulong.MaxValue;
824+
ulong heapHardLimitPOHPercent = (AppContext.GetData("GCHeapHardLimitPOHPercent") as ulong?) ?? ulong.MaxValue;
825+
RuntimeImports.GCHeapHardLimitInfo heapHardLimitInfo = new RuntimeImports.GCHeapHardLimitInfo
826+
{
827+
HeapHardLimit = heapHardLimit,
828+
HeapHardLimitPercent = heapHardLimitPercent,
829+
HeapHardLimitSOH = heapHardLimitSOH,
830+
HeapHardLimitLOH = heapHardLimitLOH,
831+
HeapHardLimitPOH = heapHardLimitPOH,
832+
HeapHardLimitSOHPercent = heapHardLimitSOHPercent,
833+
HeapHardLimitLOHPercent = heapHardLimitLOHPercent,
834+
HeapHardLimitPOHPercent = heapHardLimitPOHPercent,
835+
};
836+
RefreshMemoryStatus status = (RefreshMemoryStatus)RuntimeImports.RhRefreshMemoryLimit(heapHardLimitInfo);
837+
switch (status)
838+
{
839+
case RefreshMemoryStatus.HardLimitTooLow:
840+
throw new InvalidOperationException(SR.InvalidOperationException_HardLimitTooLow);
841+
case RefreshMemoryStatus.HardLimitInvalid:
842+
throw new InvalidOperationException(SR.InvalidOperationException_HardLimitInvalid);
843+
}
844+
Debug.Assert(status == RefreshMemoryStatus.Succeeded);
845+
}
806846
}
807847
}

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,22 @@ internal enum GCConfigurationType
201201
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
202202
internal static unsafe partial void RhEnumerateConfigurationValues(void* configurationContext, delegate* unmanaged<void*, void*, void*, GCConfigurationType, long, void> callback);
203203

204+
internal struct GCHeapHardLimitInfo
205+
{
206+
internal ulong HeapHardLimit;
207+
internal ulong HeapHardLimitPercent;
208+
internal ulong HeapHardLimitSOH;
209+
internal ulong HeapHardLimitLOH;
210+
internal ulong HeapHardLimitPOH;
211+
internal ulong HeapHardLimitSOHPercent;
212+
internal ulong HeapHardLimitLOHPercent;
213+
internal ulong HeapHardLimitPOHPercent;
214+
}
215+
216+
[LibraryImport(RuntimeLibrary)]
217+
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
218+
internal static partial int RhRefreshMemoryLimit(GCHeapHardLimitInfo heapHardLimitInfo);
219+
204220
[LibraryImport(RuntimeLibrary)]
205221
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
206222
internal static partial long RhGetTotalAllocatedBytesPrecise();

src/coreclr/vm/comutilnative.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,6 +1211,7 @@ void GCInterface::EnumerateConfigurationValues(void* configurationContext, Enume
12111211
}
12121212

12131213
GCHeapHardLimitInfo g_gcHeapHardLimitInfo;
1214+
bool g_gcHeapHardLimitInfoSpecified = false;
12141215

12151216
extern "C" int QCALLTYPE GCInterface_RefreshMemoryLimit(GCHeapHardLimitInfo heapHardLimitInfo)
12161217
{
@@ -1220,6 +1221,7 @@ extern "C" int QCALLTYPE GCInterface_RefreshMemoryLimit(GCHeapHardLimitInfo heap
12201221

12211222
BEGIN_QCALL;
12221223
g_gcHeapHardLimitInfo = heapHardLimitInfo;
1224+
g_gcHeapHardLimitInfoSpecified = true;
12231225
result = GCInterface::RefreshMemoryLimit();
12241226
END_QCALL;
12251227

src/coreclr/vm/gcenv.ee.cpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,14 +1191,17 @@ bool GCToEEInterface::GetIntConfigValue(const char* privateKey, const char* publ
11911191
return true;
11921192
}
11931193

1194-
if ((g_gcHeapHardLimitInfo.heapHardLimit != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimit") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimit; return true; }
1195-
if ((g_gcHeapHardLimitInfo.heapHardLimitPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitPercent; return true; }
1196-
if ((g_gcHeapHardLimitInfo.heapHardLimitSOH != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitSOH") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitSOH; return true; }
1197-
if ((g_gcHeapHardLimitInfo.heapHardLimitLOH != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitLOH") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitLOH; return true; }
1198-
if ((g_gcHeapHardLimitInfo.heapHardLimitPOH != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitPOH") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitPOH; return true; }
1199-
if ((g_gcHeapHardLimitInfo.heapHardLimitSOHPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitSOHPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitSOHPercent; return true; }
1200-
if ((g_gcHeapHardLimitInfo.heapHardLimitLOHPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitLOHPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitLOHPercent; return true; }
1201-
if ((g_gcHeapHardLimitInfo.heapHardLimitPOHPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitPOHPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitPOHPercent; return true; }
1194+
if (g_gcHeapHardLimitInfoSpecified)
1195+
{
1196+
if ((g_gcHeapHardLimitInfo.heapHardLimit != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimit") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimit; return true; }
1197+
if ((g_gcHeapHardLimitInfo.heapHardLimitPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitPercent; return true; }
1198+
if ((g_gcHeapHardLimitInfo.heapHardLimitSOH != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitSOH") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitSOH; return true; }
1199+
if ((g_gcHeapHardLimitInfo.heapHardLimitLOH != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitLOH") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitLOH; return true; }
1200+
if ((g_gcHeapHardLimitInfo.heapHardLimitPOH != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitPOH") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitPOH; return true; }
1201+
if ((g_gcHeapHardLimitInfo.heapHardLimitSOHPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitSOHPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitSOHPercent; return true; }
1202+
if ((g_gcHeapHardLimitInfo.heapHardLimitLOHPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitLOHPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitLOHPercent; return true; }
1203+
if ((g_gcHeapHardLimitInfo.heapHardLimitPOHPercent != UINT64_MAX) && strcmp(privateKey, "GCHeapHardLimitPOHPercent") == 0) { *value = g_gcHeapHardLimitInfo.heapHardLimitPOHPercent; return true; }
1204+
}
12021205

12031206
WCHAR configKey[MaxConfigKeyLength];
12041207
if (MultiByteToWideChar(CP_ACP, 0, privateKey, -1 /* key is null-terminated */, configKey, MaxConfigKeyLength) == 0)

0 commit comments

Comments
 (0)