Skip to content

Commit f924653

Browse files
Use Knobs to populate AppContext switches (#86883)
Previously AppContext switches were set by injecting a method that calls `AppContext.SetSwitch` at startup. Use the configuration blob added in #86068 instead. This makes startup a tiny bit faster and the outputs a tiny bit smaller. Fixes #77054.
1 parent 1f96376 commit f924653

File tree

14 files changed

+173
-377
lines changed

14 files changed

+173
-377
lines changed

src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,12 +214,11 @@ The .NET Foundation licenses this file to you under the MIT license.
214214
<IlcArg Condition="'$(CustomNativeMain)' == 'true'" Include="--splitinit" />
215215
<IlcArg Condition="$(ExportsFile) != ''" Include="--exportsfile:$(ExportsFile)" />
216216
<IlcArg Include="@(AutoInitializedAssemblies->'--initassembly:%(Identity)')" />
217-
<IlcArg Include="@(RuntimeHostConfigurationOption->'--appcontextswitch:%(Identity)=%(Value)')" />
218-
<IlcArg Include="--appcontextswitch:RUNTIME_IDENTIFIER=$(RuntimeIdentifier)" />
219217
<IlcArg Include="@(DirectPInvoke->'--directpinvoke:%(Identity)')" />
220218
<IlcArg Include="@(DirectPInvokeList->'--directpinvokelist:%(Identity)')" />
221219
<IlcArg Include="@(_TrimmerFeatureSettings->'--feature:%(Identity)=%(Value)')" />
222220
<IlcArg Include="@(RuntimeHostConfigurationOption->'--runtimeknob:%(Identity)=%(Value)')" />
221+
<IlcArg Include="--runtimeknob:RUNTIME_IDENTIFIER=$(RuntimeIdentifier)" />
223222
<IlcArg Condition="$(ServerGarbageCollection) == 'true'" Include="--runtimeopt:gcServer=1" />
224223
<IlcArg Condition="$(IlcGenerateCompleteTypeMetadata) == 'true' and $(IlcDisableReflection) != 'true'" Include="--completetypemetadata" />
225224
<IlcArg Condition="$(IlcGenerateStackTraceData) == 'true'" Include="--stacktracedata" />

src/coreclr/nativeaot/Runtime/MiscHelpers.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "GCMemoryHelpers.h"
3939
#include "GCMemoryHelpers.inl"
4040
#include "yieldprocessornormalized.h"
41+
#include "RhConfig.h"
4142

4243
COOP_PINVOKE_HELPER(void, RhDebugBreak, ())
4344
{
@@ -422,6 +423,13 @@ COOP_PINVOKE_HELPER(int32_t, RhGetProcessCpuCount, ())
422423
return PalGetProcessCpuCount();
423424
}
424425

426+
COOP_PINVOKE_HELPER(uint32_t, RhGetKnobValues, (char *** pResultKeys, char *** pResultValues))
427+
{
428+
*pResultKeys = g_pRhConfig->GetKnobNames();
429+
*pResultValues = g_pRhConfig->GetKnobValues();
430+
return g_pRhConfig->GetKnobCount();
431+
}
432+
425433
#if defined(TARGET_X86) || defined(TARGET_AMD64)
426434
EXTERN_C NATIVEAOT_API void __cdecl RhCpuIdEx(int* cpuInfo, int functionId, int subFunctionId)
427435
{

src/coreclr/nativeaot/Runtime/RhConfig.cpp

Lines changed: 22 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,8 @@ bool RhConfig::Environment::TryGetStringValue(const char* name, char** value)
120120
return true;
121121
}
122122

123-
struct CompilerEmbeddedSettingsBlob
124-
{
125-
uint32_t Size;
126-
char Data[1];
127-
};
128-
129-
extern "C" CompilerEmbeddedSettingsBlob g_compilerEmbeddedSettingsBlob;
130-
extern "C" CompilerEmbeddedSettingsBlob g_compilerEmbeddedKnobsBlob;
123+
extern "C" RhConfig::Config g_compilerEmbeddedSettingsBlob;
124+
extern "C" RhConfig::Config g_compilerEmbeddedKnobsBlob;
131125

132126
bool RhConfig::ReadConfigValue(_In_z_ const char *name, uint64_t* pValue, bool decimal)
133127
{
@@ -136,7 +130,7 @@ bool RhConfig::ReadConfigValue(_In_z_ const char *name, uint64_t* pValue, bool d
136130

137131
// Check the embedded configuration
138132
const char *embeddedValue = nullptr;
139-
if (GetEmbeddedVariable(&g_embeddedSettings, &g_compilerEmbeddedSettingsBlob, name, true, &embeddedValue))
133+
if (GetEmbeddedVariable(&g_compilerEmbeddedSettingsBlob, name, true, &embeddedValue))
140134
{
141135
*pValue = strtoull(embeddedValue, NULL, decimal ? 10 : 16);
142136
return true;
@@ -148,7 +142,7 @@ bool RhConfig::ReadConfigValue(_In_z_ const char *name, uint64_t* pValue, bool d
148142
bool RhConfig::ReadKnobUInt64Value(_In_z_ const char *name, uint64_t* pValue)
149143
{
150144
const char *embeddedValue = nullptr;
151-
if (GetEmbeddedVariable(&g_embeddedKnobs, &g_compilerEmbeddedKnobsBlob, name, false, &embeddedValue))
145+
if (GetEmbeddedVariable(&g_compilerEmbeddedKnobsBlob, name, false, &embeddedValue))
152146
{
153147
*pValue = strtoull(embeddedValue, NULL, 10);
154148
return true;
@@ -160,7 +154,7 @@ bool RhConfig::ReadKnobUInt64Value(_In_z_ const char *name, uint64_t* pValue)
160154
bool RhConfig::ReadKnobBooleanValue(_In_z_ const char *name, bool* pValue)
161155
{
162156
const char *embeddedValue = nullptr;
163-
if (GetEmbeddedVariable(&g_embeddedKnobs, &g_compilerEmbeddedKnobsBlob, name, false, &embeddedValue))
157+
if (GetEmbeddedVariable(&g_compilerEmbeddedKnobsBlob, name, false, &embeddedValue))
164158
{
165159
*pValue = strcmp(embeddedValue, "true") == 0;
166160
return true;
@@ -169,29 +163,30 @@ bool RhConfig::ReadKnobBooleanValue(_In_z_ const char *name, bool* pValue)
169163
return false;
170164
}
171165

172-
bool RhConfig::GetEmbeddedVariable(void *volatile * embeddedSettings, void* compilerEmbeddedSettingsBlob, _In_z_ const char* configName, bool caseSensitive, _Out_ const char** configValue)
166+
char** RhConfig::GetKnobNames()
173167
{
174-
// Read the config if we haven't yet
175-
if (*embeddedSettings == NULL)
176-
{
177-
ReadEmbeddedSettings(embeddedSettings, compilerEmbeddedSettingsBlob);
178-
}
168+
return g_compilerEmbeddedKnobsBlob.GetKeys();
169+
}
179170

180-
// Config wasn't read or reading failed
181-
if (*embeddedSettings == CONFIG_INI_NOT_AVAIL)
182-
{
183-
return false;
184-
}
171+
char** RhConfig::GetKnobValues()
172+
{
173+
return g_compilerEmbeddedKnobsBlob.GetValues();
174+
}
185175

186-
const ConfigPair* configPairs = (const ConfigPair*)*embeddedSettings;
176+
uint32_t RhConfig::GetKnobCount()
177+
{
178+
return g_compilerEmbeddedKnobsBlob.GetCount();
179+
}
187180

181+
bool RhConfig::GetEmbeddedVariable(Config* config, _In_z_ const char* configName, bool caseSensitive, _Out_ const char** configValue)
182+
{
188183
// Find the first name which matches
189-
for (uint32_t iSettings = 0; iSettings < ((CompilerEmbeddedSettingsBlob*)compilerEmbeddedSettingsBlob)->Size; iSettings++)
184+
for (uint32_t iSettings = 0; iSettings < config->GetCount(); iSettings++)
190185
{
191-
if ((caseSensitive && strcmp(configName, configPairs[iSettings].Key) == 0)
192-
|| (!caseSensitive && _stricmp(configName, configPairs[iSettings].Key) == 0))
186+
if ((caseSensitive && strcmp(configName, config->GetKeyAt(iSettings)) == 0)
187+
|| (!caseSensitive && _stricmp(configName, config->GetKeyAt(iSettings)) == 0))
193188
{
194-
*configValue = configPairs[iSettings].Value;
189+
*configValue = config->GetValueAt(iSettings);
195190
return true;
196191
}
197192
}
@@ -200,107 +195,4 @@ bool RhConfig::GetEmbeddedVariable(void *volatile * embeddedSettings, void* comp
200195
return false;
201196
}
202197

203-
void RhConfig::ReadEmbeddedSettings(void *volatile * embeddedSettings, void* compilerEmbeddedSettingsBlob)
204-
{
205-
if (*embeddedSettings == NULL)
206-
{
207-
uint32_t size = ((CompilerEmbeddedSettingsBlob*)compilerEmbeddedSettingsBlob)->Size;
208-
char* data = ((CompilerEmbeddedSettingsBlob*)compilerEmbeddedSettingsBlob)->Data;
209-
210-
//if reading the file contents failed set embeddedSettings to CONFIG_INI_NOT_AVAIL
211-
if (size == 0)
212-
{
213-
//only set if another thread hasn't initialized the buffer yet, otherwise ignore and let the first setter win
214-
PalInterlockedCompareExchangePointer(embeddedSettings, CONFIG_INI_NOT_AVAIL, NULL);
215-
216-
return;
217-
}
218-
219-
ConfigPair* iniBuff = new (nothrow) ConfigPair[size];
220-
if (iniBuff == NULL)
221-
{
222-
//only set if another thread hasn't initialized the buffer yet, otherwise ignore and let the first setter win
223-
PalInterlockedCompareExchangePointer(embeddedSettings, CONFIG_INI_NOT_AVAIL, NULL);
224-
225-
return;
226-
}
227-
228-
uint32_t iBuff = 0;
229-
uint32_t iIniBuff = 0;
230-
char* currLine;
231-
232-
//while we haven't reached the max number of config pairs, or the end of the file, read the next line
233-
while (iBuff < size)
234-
{
235-
currLine = &data[iBuff];
236-
237-
//find the end of the line
238-
while ((data[iBuff] != '\0') && (iBuff < size))
239-
iBuff++;
240-
241-
//parse the line
242-
//only increment iIniBuff if the parsing succeeded otherwise reuse the config struct
243-
if (ParseConfigLine(&iniBuff[iIniBuff], currLine))
244-
{
245-
iIniBuff++;
246-
}
247-
248-
//advance to the next line;
249-
iBuff++;
250-
}
251-
252-
//if another thread initialized first let the first setter win
253-
//delete the iniBuff to avoid leaking memory
254-
if (PalInterlockedCompareExchangePointer(embeddedSettings, iniBuff, NULL) != NULL)
255-
{
256-
delete[] iniBuff;
257-
}
258-
}
259-
260-
return;
261-
}
262-
263-
//Parses one line of config and populates values in the passed in configPair
264-
//returns: true if the parsing was successful, false if the parsing failed.
265-
//NOTE: if the method fails configPair is left in an uninitialized state
266-
bool RhConfig::ParseConfigLine(_Out_ ConfigPair* configPair, _In_z_ const char * line)
267-
{
268-
uint32_t iLine = 0;
269-
uint32_t iKey = 0;
270-
uint32_t iVal = 0;
271-
272-
//while we haven't reached the end of the key signalled by '=', or the end of the line, or the key maxlen
273-
while (line[iLine] != '=' && line[iLine] != '\0' && iKey < CONFIG_KEY_MAXLEN)
274-
{
275-
configPair->Key[iKey++] = line[iLine++];
276-
}
277-
278-
//if the current char is not '=' we reached the key maxlen, or the line ended return false
279-
if (line[iLine] != '=')
280-
{
281-
return FALSE;
282-
}
283-
284-
configPair->Key[iKey] = '\0';
285-
286-
//increment to start of the value
287-
iLine++;
288-
289-
//while we haven't reached the end of the line, or val maxlen
290-
while (line[iLine] != '\0' && iVal < CONFIG_VAL_MAXLEN)
291-
{
292-
configPair->Value[iVal++] = line[iLine++];
293-
}
294-
295-
//if the current char is not '\0' we didn't reach the end of the line return false
296-
if (line[iLine] != '\0')
297-
{
298-
return FALSE;
299-
}
300-
301-
configPair->Value[iVal] = '\0';
302-
303-
return TRUE;
304-
}
305-
306198
#endif

src/coreclr/nativeaot/Runtime/RhConfig.h

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,20 @@
1818

1919
class RhConfig
2020
{
21-
22-
#define CONFIG_INI_NOT_AVAIL (void*)0x1 //signal for ini file failed to load
23-
#define CONFIG_KEY_MAXLEN 50 //arbitrary max length of config keys increase if needed
2421
#define CONFIG_VAL_MAXLEN 16 //64 bit uint in hex
25-
private:
26-
struct ConfigPair
22+
public:
23+
struct Config
2724
{
25+
uint32_t m_count;
26+
char* m_first[];
2827
public:
29-
char Key[CONFIG_KEY_MAXLEN + 1]; //maxlen + null terminator
30-
char Value[CONFIG_VAL_MAXLEN + 1]; //maxlen + null terminator
28+
uint32_t GetCount() { return m_count; }
29+
char* GetKeyAt(int32_t index) { return m_first[index]; }
30+
char* GetValueAt(int32_t index) { return m_first[m_count + index]; }
31+
char** GetKeys() { return m_first; }
32+
char** GetValues() { return &m_first[m_count]; }
3133
};
3234

33-
// g_embeddedSettings is a buffer of ConfigPair structs embedded in the compiled binary.
34-
//
35-
//NOTE: g_embeddedSettings is only set in ReadEmbeddedSettings and must be set atomically only once
36-
// using PalInterlockedCompareExchangePointer to avoid races when initializing
37-
void* volatile g_embeddedSettings = NULL;
38-
void* volatile g_embeddedKnobs = NULL;
39-
40-
public:
4135
class Environment
4236
{
4337
public: // static
@@ -52,6 +46,10 @@ class RhConfig
5246
bool ReadKnobUInt64Value(_In_z_ const char* wszName, uint64_t* pValue);
5347
bool ReadKnobBooleanValue(_In_z_ const char* wszName, bool* pValue);
5448

49+
char** GetKnobNames();
50+
char** GetKnobValues();
51+
uint32_t GetKnobCount();
52+
5553
#define DEFINE_VALUE_ACCESSOR(_name, defaultVal) \
5654
uint64_t Get##_name() \
5755
{ \
@@ -99,16 +97,9 @@ class RhConfig
9997
#define CONFIG_FILE_MAXLEN RCV_Count * sizeof(ConfigPair) + 2000
10098

10199
private:
102-
//Parses one line of config and populates values in the passed in configPair
103-
//returns: true if the parsing was successful, false if the parsing failed.
104-
//NOTE: if the method fails configPair is left in an uninitialized state
105-
bool ParseConfigLine(_Out_ ConfigPair* configPair, _In_z_ const char * line);
106-
107-
void ReadEmbeddedSettings(void *volatile * embeddedSettings, void* compilerEmbeddedSettingsBlob);
108-
109100
// Gets a pointer to the embedded configuration value. Memory is held by the callee.
110101
// Returns true if the variable was found, false otherwise
111-
bool GetEmbeddedVariable(void *volatile * embeddedSettings, void* compilerEmbeddedSettingsBlob, _In_z_ const char* configName, bool caseSensitive, _Out_ const char** configValue);
102+
bool GetEmbeddedVariable(Config* config, _In_z_ const char* configName, bool caseSensitive, _Out_ const char** configValue);
112103

113104
uint32_t m_uiConfigValuesRead;
114105
uint64_t m_uiConfigValues[RCV_Count];

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,30 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Collections.Generic;
45
using System.Runtime;
56
using System.Runtime.ExceptionServices;
7+
using System.Text;
68

79
namespace System
810
{
911
public static partial class AppContext
1012
{
13+
private static unsafe Dictionary<string, object?> InitializeDataStore()
14+
{
15+
uint count = RuntimeImports.RhGetKnobValues(out byte** keys, out byte** values);
16+
17+
var dataStore = new Dictionary<string, object?>((int)count);
18+
for (int i = 0; i < count; i++)
19+
{
20+
dataStore.Add(
21+
Encoding.UTF8.GetString(keys[i], string.strlen(keys[i])),
22+
Encoding.UTF8.GetString(values[i], string.strlen(values[i])));
23+
}
24+
25+
return dataStore;
26+
}
27+
1128
[RuntimeExport("OnFirstChanceException")]
1229
internal static void OnFirstChanceException(object e)
1330
{

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,10 @@ internal static IntPtr RhGetModuleSection(TypeManagerHandle module, ReadyToRunSe
553553
[RuntimeImport(RuntimeLibrary, "RhGetLoadedOSModules")]
554554
internal static extern uint RhGetLoadedOSModules(IntPtr[] resultArray);
555555

556+
[MethodImplAttribute(MethodImplOptions.InternalCall)]
557+
[RuntimeImport(RuntimeLibrary, "RhGetKnobValues")]
558+
internal static extern unsafe uint RhGetKnobValues(out byte** keyArray, out byte** valueArray);
559+
556560
[MethodImplAttribute(MethodImplOptions.InternalCall)]
557561
[RuntimeImport(RuntimeLibrary, "RhGetOSModuleFromPointer")]
558562
internal static extern IntPtr RhGetOSModuleFromPointer(IntPtr pointerVal);

0 commit comments

Comments
 (0)