diff options
author | Michal Strehovský <MichalStrehovsky@users.noreply.github.com> | 2018-07-17 20:59:53 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-07-17 20:59:53 +0300 |
commit | 2e533d85ee6c7922c97e2e59945d86d2d696ebcd (patch) | |
tree | e0a818350ca888f562b1f28a113ee05a7368c5ec /src/Native | |
parent | c1e97380862b2281763b34462627a86187f47bea (diff) |
Add support for embedding runtime configuration (#6103)
Runtime configuration (e.g. whether to use server GC) can currently be provided either through environment variables at runtime, or through a RhConfig.ini file placed next to the executable. This adds another channel where we burn a blob similar to RhConfig.ini format into the executable. The order of precedence is environment variable > RhConfig.ini > embedded config.
The `ServerGarbageCollection` project property that is used to determine whether to link with a runtime that supports server GC will be also used to control whether to generate a configuration that enables server GC at runtime by default.
Fixes #6100.
Diffstat (limited to 'src/Native')
-rw-r--r-- | src/Native/Runtime/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/Native/Runtime/RhConfig.cpp | 119 | ||||
-rw-r--r-- | src/Native/Runtime/RhConfig.h | 16 |
3 files changed, 131 insertions, 5 deletions
diff --git a/src/Native/Runtime/CMakeLists.txt b/src/Native/Runtime/CMakeLists.txt index f53ed3180..b65011432 100644 --- a/src/Native/Runtime/CMakeLists.txt +++ b/src/Native/Runtime/CMakeLists.txt @@ -185,6 +185,7 @@ add_definitions(-DCORERT) add_definitions(-DFEATURE_CACHED_INTERFACE_DISPATCH) add_definitions(-D_LIB) add_definitions(-DEETYPE_TYPE_MANAGER) +add_definitions(-DFEATURE_EMBEDDED_CONFIG) if(WIN32) # There is a problem with undefined symbol g_pConfig, windows don't care since it is in template method, but clang does diff --git a/src/Native/Runtime/RhConfig.cpp b/src/Native/Runtime/RhConfig.cpp index f1879ab10..fb8575407 100644 --- a/src/Native/Runtime/RhConfig.cpp +++ b/src/Native/Runtime/RhConfig.cpp @@ -34,14 +34,20 @@ UInt32 RhConfig::ReadConfigValue(_In_z_ const TCHAR *wszName, UInt32 uiDefaultVa UInt32 cchResult = 0; -#ifdef RH_ENVIRONMENT_VARIABLE_CONFIG_ENABLED +#ifdef FEATURE_ENVIRONMENT_VARIABLE_CONFIG cchResult = PalGetEnvironmentVariable(wszName, wszBuffer, cchBuffer); -#endif // RH_ENVIRONMENT_VARIABLE_CONFIG_ENABLED +#endif // FEATURE_ENVIRONMENT_VARIABLE_CONFIG //if the config key wasn't found in the environment if ((cchResult == 0) || (cchResult >= cchBuffer)) cchResult = GetIniVariable(wszName, wszBuffer, cchBuffer); +#ifdef FEATURE_EMBEDDED_CONFIG + // if the config key wasn't found in the ini file + if ((cchResult == 0) || (cchResult >= cchBuffer)) + cchResult = GetEmbeddedVariable(wszName, wszBuffer, cchBuffer); +#endif // FEATURE_EMBEDDED_CONFIG + if ((cchResult == 0) || (cchResult >= cchBuffer)) return uiDefaultValue; // not found, return default @@ -90,10 +96,40 @@ UInt32 RhConfig::GetIniVariable(_In_z_ const TCHAR* configName, _Out_writes_all_ return 0; } + return GetConfigVariable(configName, (ConfigPair*)g_iniSettings, outputBuffer, cchOutputBuffer); +} + +#ifdef FEATURE_EMBEDDED_CONFIG +UInt32 RhConfig::GetEmbeddedVariable(_In_z_ const TCHAR* configName, _Out_writes_all_(cchOutputBuffer) TCHAR* outputBuffer, _In_ UInt32 cchOutputBuffer) +{ + //the buffer needs to be big enough to read the value buffer + null terminator + if (cchOutputBuffer < CONFIG_VAL_MAXLEN + 1) + { + return 0; + } + + //if we haven't read the config yet try to read + if (g_embeddedSettings == NULL) + { + ReadEmbeddedSettings(); + } + + //if the config wasn't read or reading failed return 0 immediately + if (g_embeddedSettings == CONFIG_INI_NOT_AVAIL) + { + return 0; + } + + return GetConfigVariable(configName, (ConfigPair*)g_embeddedSettings, outputBuffer, cchOutputBuffer); +} +#endif // FEATURE_EMBEDDED_CONFIG + +UInt32 RhConfig::GetConfigVariable(_In_z_ const TCHAR* configName, const ConfigPair* configPairs, _Out_writes_all_(cchOutputBuffer) TCHAR* outputBuffer, _In_ UInt32 cchOutputBuffer) +{ //find the first name which matches (case insensitive to be compat with environment variable counterpart) for (int iSettings = 0; iSettings < RCV_Count; iSettings++) { - if (_tcsicmp(configName, ((ConfigPair*)g_iniSettings)[iSettings].Key) == 0) + if (_tcsicmp(configName, configPairs[iSettings].Key) == 0) { bool nullTerm = FALSE; @@ -101,7 +137,7 @@ UInt32 RhConfig::GetIniVariable(_In_z_ const TCHAR* configName, _Out_writes_all_ for (iValue = 0; (iValue < CONFIG_VAL_MAXLEN + 1) && (iValue < (Int32)cchOutputBuffer); iValue++) { - outputBuffer[iValue] = ((ConfigPair*)g_iniSettings)[iSettings].Value[iValue]; + outputBuffer[iValue] = configPairs[iSettings].Value[iValue]; if (outputBuffer[iValue] == '\0') { @@ -218,6 +254,81 @@ void RhConfig::ReadConfigIni() return; } +#ifdef FEATURE_EMBEDDED_CONFIG +struct CompilerEmbeddedSettingsBlob +{ + UInt32 Size; + char Data[1]; +}; + +extern "C" CompilerEmbeddedSettingsBlob g_compilerEmbeddedSettingsBlob; + +void RhConfig::ReadEmbeddedSettings() +{ + if (g_embeddedSettings == NULL) + { + //if reading the file contents failed set g_embeddedSettings to CONFIG_INI_NOT_AVAIL + if (g_compilerEmbeddedSettingsBlob.Size == 0) + { + //only set if another thread hasn't initialized the buffer yet, otherwise ignore and let the first setter win + PalInterlockedCompareExchangePointer(&g_embeddedSettings, CONFIG_INI_NOT_AVAIL, NULL); + + return; + } + + ConfigPair* iniBuff = new (nothrow) ConfigPair[RCV_Count]; + if (iniBuff == NULL) + { + //only set if another thread hasn't initialized the buffer yet, otherwise ignore and let the first setter win + PalInterlockedCompareExchangePointer(&g_embeddedSettings, CONFIG_INI_NOT_AVAIL, NULL); + + return; + } + + UInt32 iBuff = 0; + UInt32 iIniBuff = 0; + char* currLine; + + //while we haven't reached the max number of config pairs, or the end of the file, read the next line + while (iIniBuff < RCV_Count && iBuff < g_compilerEmbeddedSettingsBlob.Size) + { + currLine = &g_compilerEmbeddedSettingsBlob.Data[iBuff]; + + //find the end of the line + while ((g_compilerEmbeddedSettingsBlob.Data[iBuff] != '\0') && (iBuff < g_compilerEmbeddedSettingsBlob.Size)) + iBuff++; + + //parse the line + //only increment iIniBuff if the parsing succeeded otherwise reuse the config struct + if (ParseConfigLine(&iniBuff[iIniBuff], currLine)) + { + iIniBuff++; + } + + //advance to the next line; + iBuff++; + } + + //initialize the remaining config pairs to "\0" + while (iIniBuff < RCV_Count) + { + iniBuff[iIniBuff].Key[0] = '\0'; + iniBuff[iIniBuff].Value[0] = '\0'; + iIniBuff++; + } + + //if another thread initialized first let the first setter win + //delete the iniBuff to avoid leaking memory + if (PalInterlockedCompareExchangePointer(&g_embeddedSettings, iniBuff, NULL) != NULL) + { + delete[] iniBuff; + } + } + + return; +} +#endif // FEATURE_EMBEDDED_CONFIG + //returns the path to the runtime configuration ini _Ret_maybenull_z_ TCHAR* RhConfig::GetConfigPath() { diff --git a/src/Native/Runtime/RhConfig.h b/src/Native/Runtime/RhConfig.h index 677d97516..b5879c386 100644 --- a/src/Native/Runtime/RhConfig.h +++ b/src/Native/Runtime/RhConfig.h @@ -21,7 +21,7 @@ #ifndef DACCESS_COMPILE #if defined(_DEBUG) || !defined(APP_LOCAL_RUNTIME) -#define RH_ENVIRONMENT_VARIABLE_CONFIG_ENABLED +#define FEATURE_ENVIRONMENT_VARIABLE_CONFIG #endif class RhConfig @@ -53,6 +53,12 @@ private: private: void* volatile g_iniSettings = NULL; +#ifdef FEATURE_EMBEDDED_CONFIG + // g_embeddedSettings works similarly to g_iniSettings, except the source of the data + // is a data blob generated by the compiler and embedded into the executable. + void* volatile g_embeddedSettings = NULL; +#endif // FEATURE_EMBEDDED_CONFIG + public: #define DEFINE_VALUE_ACCESSOR(_name, defaultVal) \ @@ -123,6 +129,14 @@ private: //cchOutputBuffer is the maximum number of characters to write to outputBuffer UInt32 GetIniVariable(_In_z_ const TCHAR* configName, _Out_writes_all_(cchOutputBuffer) TCHAR* outputBuffer, _In_ UInt32 cchOutputBuffer); +#ifdef FEATURE_EMBEDDED_CONFIG + void ReadEmbeddedSettings(); + + UInt32 GetEmbeddedVariable(_In_z_ const TCHAR* configName, _Out_writes_all_(cchOutputBuffer) TCHAR* outputBuffer, _In_ UInt32 cchOutputBuffer); +#endif // FEATURE_EMBEDDED_CONFIG + + UInt32 GetConfigVariable(_In_z_ const TCHAR* configName, const ConfigPair* configPairs, _Out_writes_all_(cchOutputBuffer) TCHAR* outputBuffer, _In_ UInt32 cchOutputBuffer); + static bool priv_isspace(char c) { return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'); |