From 64d60f71cebe47994352dd4f9e2b7d942231935c Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Thu, 9 Sep 2021 11:14:00 +0200 Subject: [PATCH 1/2] Process-wide caching of ToolsetConfigurationSection to eliminate multiple loading MSBuild.exe.config --- .../Definition/ToolsetConfigurationReader.cs | 19 +++++++++ src/Shared/ToolsetElement.cs | 42 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/src/Build/Definition/ToolsetConfigurationReader.cs b/src/Build/Definition/ToolsetConfigurationReader.cs index 5665b1aaf09..1a58f901f2c 100644 --- a/src/Build/Definition/ToolsetConfigurationReader.cs +++ b/src/Build/Definition/ToolsetConfigurationReader.cs @@ -41,6 +41,13 @@ internal class ToolsetConfigurationReader : ToolsetReader /// private static readonly char[] s_separatorForExtensionsPathSearchPaths = MSBuildConstants.SemicolonChar; + /// + /// Caching MSBuild exe configuration. + /// Used only by ReadApplicationConfiguration factory function (default) as oppose to unit tests config factory functions + /// which must not cache configs. + /// + private static readonly Lazy s_configurationCache = new Lazy(ReadOpenMappedExeConfiguration); + /// /// Cached values of tools version -> project import search paths table /// @@ -250,6 +257,18 @@ protected override IEnumerable GetSubToolsetPropertyD /// Unit tests wish to avoid reading (nunit.exe) application configuration file. /// private static Configuration ReadApplicationConfiguration() + { + if (Environment.GetEnvironmentVariable("MSBUILDCACHETOOLSETCONFIGURATION") != "0") + { + return s_configurationCache.Value; + } + else + { + return ReadOpenMappedExeConfiguration(); + } + } + + private static Configuration ReadOpenMappedExeConfiguration() { // When running from the command-line or from VS, use the msbuild.exe.config file. if (BuildEnvironmentHelper.Instance.Mode != BuildEnvironmentMode.None && diff --git a/src/Shared/ToolsetElement.cs b/src/Shared/ToolsetElement.cs index ff7d9685aa7..f0be18719f4 100644 --- a/src/Shared/ToolsetElement.cs +++ b/src/Shared/ToolsetElement.cs @@ -15,7 +15,49 @@ namespace Microsoft.Build.Evaluation /// internal static class ToolsetConfigurationReaderHelpers { + /// + /// Lock for process wide ToolsetConfigurationSection section cache + /// + private static readonly object s_syncLock = new(); + + /// + /// Process wide ToolsetConfigurationSection section cache + /// + private static ToolsetConfigurationSection s_toolsetConfigurationSectionCache; + private static Configuration s_configurationOfCachedSection; + internal static ToolsetConfigurationSection ReadToolsetConfigurationSection(Configuration configuration) + { + if (Environment.GetEnvironmentVariable("MSBUILDCACHETOOLSETCONFIGURATION") != "0") + { + if (configuration == null) + { + return null; + } + + lock (s_syncLock) + { + // Cache 1st requested configuration section. In unit tests, different Configuration is provided for particular test cases. + // During runtime, however, only MSBuild exe configuration file is provided to read toolset configuration from, + // and modifying MSBuild exe configuration during lifetime of msbuild nodes is neither expected nor supported. + if (s_toolsetConfigurationSectionCache == null) + { + s_toolsetConfigurationSectionCache = GetToolsetConfigurationSection(configuration); + s_configurationOfCachedSection = configuration; + } + + return s_configurationOfCachedSection == configuration ? + s_toolsetConfigurationSectionCache : + GetToolsetConfigurationSection(configuration); + } + } + else + { + return GetToolsetConfigurationSection(configuration); + } + } + + private static ToolsetConfigurationSection GetToolsetConfigurationSection(Configuration configuration) { ToolsetConfigurationSection configurationSection = null; From e618d4d7b01db55f0a0b867da2f06d9c69909501 Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Mon, 13 Sep 2021 08:24:19 +0200 Subject: [PATCH 2/2] Moved into ChangeWave --- src/Build/Definition/ToolsetConfigurationReader.cs | 3 ++- src/Shared/ToolsetElement.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Build/Definition/ToolsetConfigurationReader.cs b/src/Build/Definition/ToolsetConfigurationReader.cs index 1a58f901f2c..990a8b686cb 100644 --- a/src/Build/Definition/ToolsetConfigurationReader.cs +++ b/src/Build/Definition/ToolsetConfigurationReader.cs @@ -10,6 +10,7 @@ using Microsoft.Build.Execution; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; +using Microsoft.Build.Utilities; using ErrorUtilities = Microsoft.Build.Shared.ErrorUtilities; using InvalidToolsetDefinitionException = Microsoft.Build.Exceptions.InvalidToolsetDefinitionException; @@ -258,7 +259,7 @@ protected override IEnumerable GetSubToolsetPropertyD /// private static Configuration ReadApplicationConfiguration() { - if (Environment.GetEnvironmentVariable("MSBUILDCACHETOOLSETCONFIGURATION") != "0") + if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0)) { return s_configurationCache.Value; } diff --git a/src/Shared/ToolsetElement.cs b/src/Shared/ToolsetElement.cs index f0be18719f4..9902fd49a06 100644 --- a/src/Shared/ToolsetElement.cs +++ b/src/Shared/ToolsetElement.cs @@ -7,6 +7,7 @@ using System.IO; using Microsoft.Build.Collections; using Microsoft.Build.Shared; +using Microsoft.Build.Utilities; namespace Microsoft.Build.Evaluation { @@ -28,7 +29,7 @@ internal static class ToolsetConfigurationReaderHelpers internal static ToolsetConfigurationSection ReadToolsetConfigurationSection(Configuration configuration) { - if (Environment.GetEnvironmentVariable("MSBUILDCACHETOOLSETCONFIGURATION") != "0") + if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0)) { if (configuration == null) {