diff --git a/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs b/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs index 1edc42937852..1a547dd45ebb 100644 --- a/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs @@ -382,26 +382,26 @@ public void EmitRangeAttribute(string modifier, string prefix, string className, string initializationString = emitTimeSpanSupport ? """ - if (OperandType == typeof(global::System.TimeSpan)) - { - if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || - !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) - { - throw new global::System.InvalidOperationException(c_minMaxError); - } - Minimum = timeSpanMinimum; - Maximum = timeSpanMaximum; - } - else - { - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - } + if (OperandType == typeof(global::System.TimeSpan)) + { + if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || + !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + Minimum = timeSpanMinimum; + Maximum = timeSpanMaximum; + } + else + { + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } """ : """ - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); """; string convertValue = emitTimeSpanSupport ? @@ -470,7 +470,7 @@ public void EmitRangeAttribute(string modifier, string prefix, string className, public {{qualifiedClassName}}(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -483,33 +483,40 @@ public void EmitRangeAttribute(string modifier, string prefix, string className, public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) - { - throw new global::System.InvalidOperationException(c_minMaxError); - } - if (NeedToConvertMinMax) + lock (_lock) { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + if (!_initialized) + { + if (Minimum is null || Maximum is null) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; {{initializationString}} + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; + } } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); - } - Initialized = true; } if (value is null or string { Length: 0 }) diff --git a/src/libraries/Microsoft.Extensions.Options/src/Microsoft.Extensions.Options.csproj b/src/libraries/Microsoft.Extensions.Options/src/Microsoft.Extensions.Options.csproj index de066111eb57..e35606898e39 100644 --- a/src/libraries/Microsoft.Extensions.Options/src/Microsoft.Extensions.Options.csproj +++ b/src/libraries/Microsoft.Extensions.Options/src/Microsoft.Extensions.Options.csproj @@ -4,8 +4,8 @@ $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.1;netstandard2.0;$(NetFrameworkMinimum) true true - false - 1 + true + 2 Provides a strongly typed way of specifying and accessing settings using dependency injection. diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netcore.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netcore.g.cs index 38bacf966df0..b36fff7e4906 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netcore.g.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netcore.g.cs @@ -84,7 +84,7 @@ public __SourceGen__RangeAttribute(double minimum, double maximum) : base() public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -97,34 +97,41 @@ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, str public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) + lock (_lock) { - throw new global::System.InvalidOperationException(c_minMaxError); + if (!_initialized) + { + if (Minimum is null || Maximum is null) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; + } } - if (NeedToConvertMinMax) - { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); - } - Initialized = true; } if (value is null or string { Length: 0 }) diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netfx.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netfx.g.cs index fe77e3e6bd92..1fa2f9c2e257 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netfx.g.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netfx.g.cs @@ -82,7 +82,7 @@ public __SourceGen__RangeAttribute(double minimum, double maximum) : base() public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -95,34 +95,41 @@ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, str public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) + lock (_lock) { - throw new global::System.InvalidOperationException(c_minMaxError); + if (!_initialized) + { + if (Minimum is null || Maximum is null) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; + } } - if (NeedToConvertMinMax) - { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); - } - Initialized = true; } if (value is null or string { Length: 0 }) diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang10.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang10.g.cs index 7cf1fe61e1a9..789d299cf93c 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang10.g.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang10.g.cs @@ -410,7 +410,7 @@ public __SourceGen__2C497155_RangeAttribute(double minimum, double maximum) : ba public __SourceGen__2C497155_RangeAttribute(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -423,47 +423,54 @@ public __SourceGen__2C497155_RangeAttribute(global::System.Type type, string min public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) + lock (_lock) { - throw new global::System.InvalidOperationException(c_minMaxError); - } - if (NeedToConvertMinMax) - { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; - if (OperandType == typeof(global::System.TimeSpan)) + if (!_initialized) { - if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || - !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + if (Minimum is null || Maximum is null) { - throw new global::System.InvalidOperationException(c_minMaxError); + throw new global::System.InvalidOperationException(MinMaxError); } - Minimum = timeSpanMinimum; - Maximum = timeSpanMaximum; - } - else - { - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + if (OperandType == typeof(global::System.TimeSpan)) + { + if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || + !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + Minimum = timeSpanMinimum; + Maximum = timeSpanMaximum; + } + else + { + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; } } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); - } - Initialized = true; } if (value is null or string { Length: 0 }) diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang11.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang11.g.cs index f7bba0460334..60d511f2e835 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang11.g.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang11.g.cs @@ -410,7 +410,7 @@ public __SourceGen__RangeAttribute(double minimum, double maximum) : base() public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -423,47 +423,54 @@ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, str public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) + lock (_lock) { - throw new global::System.InvalidOperationException(c_minMaxError); - } - if (NeedToConvertMinMax) - { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; - if (OperandType == typeof(global::System.TimeSpan)) + if (!_initialized) { - if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || - !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + if (Minimum is null || Maximum is null) { - throw new global::System.InvalidOperationException(c_minMaxError); + throw new global::System.InvalidOperationException(MinMaxError); } - Minimum = timeSpanMinimum; - Maximum = timeSpanMaximum; - } - else - { - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + if (OperandType == typeof(global::System.TimeSpan)) + { + if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || + !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + Minimum = timeSpanMinimum; + Maximum = timeSpanMaximum; + } + else + { + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; } } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); - } - Initialized = true; } if (value is null or string { Length: 0 }) diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang10.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang10.g.cs index 4b28eb159d14..9c20532b1763 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang10.g.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang10.g.cs @@ -325,7 +325,7 @@ public __SourceGen__2C497155_RangeAttribute(double minimum, double maximum) : ba public __SourceGen__2C497155_RangeAttribute(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -338,47 +338,54 @@ public __SourceGen__2C497155_RangeAttribute(global::System.Type type, string min public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) + lock (_lock) { - throw new global::System.InvalidOperationException(c_minMaxError); - } - if (NeedToConvertMinMax) - { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; - if (OperandType == typeof(global::System.TimeSpan)) + if (!_initialized) { - if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || - !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + if (Minimum is null || Maximum is null) { - throw new global::System.InvalidOperationException(c_minMaxError); + throw new global::System.InvalidOperationException(MinMaxError); } - Minimum = timeSpanMinimum; - Maximum = timeSpanMaximum; - } - else - { - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + if (OperandType == typeof(global::System.TimeSpan)) + { + if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || + !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + Minimum = timeSpanMinimum; + Maximum = timeSpanMaximum; + } + else + { + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; } } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); - } - Initialized = true; } if (value is null or string { Length: 0 }) diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang11.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang11.g.cs index 4c300abc6d05..c563c65e8219 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang11.g.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang11.g.cs @@ -325,7 +325,7 @@ public __SourceGen__RangeAttribute(double minimum, double maximum) : base() public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -338,47 +338,54 @@ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, str public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) + lock (_lock) { - throw new global::System.InvalidOperationException(c_minMaxError); - } - if (NeedToConvertMinMax) - { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; - if (OperandType == typeof(global::System.TimeSpan)) + if (!_initialized) { - if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || - !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + if (Minimum is null || Maximum is null) { - throw new global::System.InvalidOperationException(c_minMaxError); + throw new global::System.InvalidOperationException(MinMaxError); } - Minimum = timeSpanMinimum; - Maximum = timeSpanMaximum; - } - else - { - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + if (OperandType == typeof(global::System.TimeSpan)) + { + if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || + !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + Minimum = timeSpanMinimum; + Maximum = timeSpanMaximum; + } + else + { + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; } } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); - } - Initialized = true; } if (value is null or string { Length: 0 }) diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/OptionsRuntimeTests.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/OptionsRuntimeTests.cs index 4c701e4b9f49..8487570d208b 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/OptionsRuntimeTests.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/OptionsRuntimeTests.cs @@ -7,6 +7,7 @@ using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Xunit; @@ -399,6 +400,23 @@ public void TestCustomGeneratedAttributes() Assert.Equal(results.Count(), generatorResult.Failures.Count()); } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public void TestGeneratedRangeAttributeThreadSafety() + { + OptionsWithTimeSpanRangeAttribute options = new OptionsWithTimeSpanRangeAttribute() { Name = "T1", Period = TimeSpan.FromHours(1) }; + TimeSpanRangeAttributeValidator validator = new TimeSpanRangeAttributeValidator(); + + var barrier = new Barrier(8); + Task.WaitAll( + (from i in Enumerable.Range(0, barrier.ParticipantCount) + select Task.Factory.StartNew(() => + { + barrier.SignalAndWait(); + ValidateOptionsResult result = validator.Validate("T1", options); + Assert.True(result.Succeeded); + }, TaskCreationOptions.LongRunning)).ToArray()); + } } public class FakeCount(int count) { public int Count { get { return count; } } } @@ -605,4 +623,17 @@ public partial class NewAttributesValidator : IValidateOptions + { + } } diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetCoreApp/Validators.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetCoreApp/Validators.g.cs index 93c101431004..81a68c390164 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetCoreApp/Validators.g.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetCoreApp/Validators.g.cs @@ -1,4 +1,4 @@ - + // #nullable enable #pragma warning disable CS1591 // Compensate for https://github.com/dotnet/roslyn/issues/54103 @@ -2195,7 +2195,7 @@ public __SourceGen__RangeAttribute(double minimum, double maximum) : base() public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -2208,47 +2208,54 @@ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, str public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) - { - throw new global::System.InvalidOperationException(c_minMaxError); - } - if (NeedToConvertMinMax) + lock (_lock) { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; - if (OperandType == typeof(global::System.TimeSpan)) + if (!_initialized) { - if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || - !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + if (Minimum is null || Maximum is null) { - throw new global::System.InvalidOperationException(c_minMaxError); + throw new global::System.InvalidOperationException(MinMaxError); } - Minimum = timeSpanMinimum; - Maximum = timeSpanMaximum; - } - else - { - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + if (OperandType == typeof(global::System.TimeSpan)) + { + if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || + !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + Minimum = timeSpanMinimum; + Maximum = timeSpanMaximum; + } + else + { + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; } } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); - } - Initialized = true; } if (value is null or string { Length: 0 }) diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetFX/Validators.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetFX/Validators.g.cs index 3c9f86fd84f8..7c388c228a76 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetFX/Validators.g.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetFX/Validators.g.cs @@ -1,4 +1,4 @@ - + // #nullable enable #pragma warning disable CS1591 // Compensate for https://github.com/dotnet/roslyn/issues/54103 @@ -2087,7 +2087,7 @@ public __SourceGen__RangeAttribute(double minimum, double maximum) : base() public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -2100,47 +2100,54 @@ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, str public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) - { - throw new global::System.InvalidOperationException(c_minMaxError); - } - if (NeedToConvertMinMax) + lock (_lock) { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; - if (OperandType == typeof(global::System.TimeSpan)) + if (!_initialized) { - if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || - !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + if (Minimum is null || Maximum is null) { - throw new global::System.InvalidOperationException(c_minMaxError); + throw new global::System.InvalidOperationException(MinMaxError); } - Minimum = timeSpanMinimum; - Maximum = timeSpanMaximum; - } - else - { - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + if (OperandType == typeof(global::System.TimeSpan)) + { + if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || + !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + Minimum = timeSpanMinimum; + Maximum = timeSpanMaximum; + } + else + { + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; } } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); - } - Initialized = true; } if (value is null or string { Length: 0 })