diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemfdCreate.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemfdCreate.cs index 5fdca42fdb4aae..5bb9fb976d3010 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemfdCreate.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemfdCreate.cs @@ -1,8 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Runtime.InteropServices; -using System.Threading; using Microsoft.Win32.SafeHandles; internal static partial class Interop @@ -15,19 +15,18 @@ internal static partial class Sys [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_IsMemfdSupported", SetLastError = true)] private static partial int MemfdSupportedImpl(); - private static volatile sbyte s_memfdSupported; + private static volatile NullableBool s_memfdSupported; internal static bool IsMemfdSupported { get { - sbyte memfdSupported = s_memfdSupported; - if (memfdSupported == 0) + NullableBool memfdSupported = s_memfdSupported; + if (memfdSupported == NullableBool.Undefined) { - Interlocked.CompareExchange(ref s_memfdSupported, (sbyte)(MemfdSupportedImpl() == 1 ? 1 : -1), 0); - memfdSupported = s_memfdSupported; + s_memfdSupported = memfdSupported = MemfdSupportedImpl() == 1 ? NullableBool.True : NullableBool.False; } - return memfdSupported > 0; + return memfdSupported == NullableBool.True; } } } diff --git a/src/libraries/Common/src/System/Console/ConsoleUtils.cs b/src/libraries/Common/src/System/Console/ConsoleUtils.cs index f8472390d1f858..10f0b167c7eb6e 100644 --- a/src/libraries/Common/src/System/Console/ConsoleUtils.cs +++ b/src/libraries/Common/src/System/Console/ConsoleUtils.cs @@ -6,18 +6,18 @@ namespace System internal static partial class ConsoleUtils { /// Whether to output ansi color strings. - private static volatile int s_emitAnsiColorCodes = -1; + private static volatile NullableBool s_emitAnsiColorCodes; /// Get whether to emit ANSI color codes. public static bool EmitAnsiColorCodes { get { - // The flag starts at -1. If it's no longer -1, it's 0 or 1 to represent false or true. - int emitAnsiColorCodes = s_emitAnsiColorCodes; - if (emitAnsiColorCodes != -1) + // The flag starts at Undefined. If it's no longer Undefined, it's False or True. + NullableBool emitAnsiColorCodes = s_emitAnsiColorCodes; + if (emitAnsiColorCodes != NullableBool.Undefined) { - return Convert.ToBoolean(emitAnsiColorCodes); + return emitAnsiColorCodes == NullableBool.True; } // We've not yet computed whether to emit codes or not. Do so now. We may race with @@ -43,7 +43,7 @@ public static bool EmitAnsiColorCodes } // Store and return the computed answer. - s_emitAnsiColorCodes = Convert.ToInt32(enabled); + s_emitAnsiColorCodes = enabled ? NullableBool.True : NullableBool.False; return enabled; } } diff --git a/src/libraries/Common/src/System/NullableBool.cs b/src/libraries/Common/src/System/NullableBool.cs new file mode 100644 index 00000000000000..e271ae96b1a29c --- /dev/null +++ b/src/libraries/Common/src/System/NullableBool.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System +{ + /// + /// Used instead of bool? for thread-safe state because is not guaranteed to be read or written atomically. + /// + internal enum NullableBool : sbyte + { + Undefined = 0, + False = -1, + True = 1, + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj b/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj index df94b82a0df8fc..ad8414ecec2137 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj @@ -26,6 +26,7 @@ + diff --git a/src/libraries/System.Console/src/System.Console.csproj b/src/libraries/System.Console/src/System.Console.csproj index 802f7e0432dd4c..341c9947b9c829 100644 --- a/src/libraries/System.Console/src/System.Console.csproj +++ b/src/libraries/System.Console/src/System.Console.csproj @@ -199,6 +199,8 @@ + diff --git a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj index 0342c73e632f25..f89dbf1fab2d31 100644 --- a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj +++ b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj @@ -308,6 +308,8 @@ + Gets the IDs of all processes on the current machine. public static int[] GetProcessIds() => new List(EnumerateProcessIds()).ToArray(); @@ -266,11 +266,7 @@ internal static bool ProcMatchesPidNamespace { get { - // _procMatchesPidNamespace is set to: - // - 0: when uninitialized, - // - 1: '/proc' and the process pid namespace match, - // - 2: when they don't match. - if (_procMatchesPidNamespace == 0) + if (_procMatchesPidNamespace == NullableBool.Undefined) { // '/proc/self' is a symlink to the pid used by '/proc' for the current process. // We compare it with the pid of the current process to see if the '/proc' and pid namespace match up. @@ -282,9 +278,9 @@ internal static bool ProcMatchesPidNamespace } Debug.Assert(procSelfPid.HasValue); - _procMatchesPidNamespace = !procSelfPid.HasValue || procSelfPid == Environment.ProcessId ? 1 : 2; + _procMatchesPidNamespace = !procSelfPid.HasValue || procSelfPid == Environment.ProcessId ? NullableBool.True : NullableBool.False; } - return _procMatchesPidNamespace == 1; + return _procMatchesPidNamespace == NullableBool.True; } } } diff --git a/src/libraries/System.IO.MemoryMappedFiles/src/System.IO.MemoryMappedFiles.csproj b/src/libraries/System.IO.MemoryMappedFiles/src/System.IO.MemoryMappedFiles.csproj index 8e01c1fab825bb..145320fcf0861e 100644 --- a/src/libraries/System.IO.MemoryMappedFiles/src/System.IO.MemoryMappedFiles.csproj +++ b/src/libraries/System.IO.MemoryMappedFiles/src/System.IO.MemoryMappedFiles.csproj @@ -121,6 +121,8 @@ Link="Common\Interop\Unix\Interop.MAdvise.cs" /> + + + diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Windows.cs b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Windows.cs index 6fa808a428cd26..18b669cadd2152 100644 --- a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Windows.cs +++ b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Windows.cs @@ -15,19 +15,17 @@ namespace System.Net { internal static partial class NameResolutionPal { - private static volatile int s_getAddrInfoExSupported; + private static volatile NullableBool s_getAddrInfoExSupported; public static bool SupportsGetAddrInfoAsync { get { - int supported = s_getAddrInfoExSupported; - if (supported == 0) + if (s_getAddrInfoExSupported == NullableBool.Undefined) { Initialize(); - supported = s_getAddrInfoExSupported; } - return supported == 1; + return s_getAddrInfoExSupported == NullableBool.True; static void Initialize() { @@ -39,7 +37,7 @@ static void Initialize() // We can't just check that 'GetAddrInfoEx' exists, because it existed before supporting overlapped. // The existence of 'GetAddrInfoExCancel' indicates that overlapped is supported. bool supported = NativeLibrary.TryGetExport(libHandle, Interop.Winsock.GetAddrInfoExCancelFunctionName, out _); - Interlocked.CompareExchange(ref s_getAddrInfoExSupported, supported ? 1 : -1, 0); + s_getAddrInfoExSupported = supported ? NullableBool.True : NullableBool.False; } } } diff --git a/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj b/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj index 7daa5977519b06..378012a38768ca 100644 --- a/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj +++ b/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj @@ -39,6 +39,8 @@ + + diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs index 582c0a3f22c313..29bd3092b2bf2a 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs @@ -21,33 +21,33 @@ public partial class SslStream private const string EnableServerAiaDownloadsCtxSwitch = "System.Net.Security.EnableServerAiaDownloads"; private const string EnableServerAiaDownloadsEnvironmentVariable = "DOTNET_SYSTEM_NET_SECURITY_ENABLESERVERAIADOWNLOADS"; - private static volatile int s_disableTlsResume = -1; - private static volatile int s_enableServerAiaDownloads = -1; + private static volatile NullableBool s_disableTlsResume; + private static volatile NullableBool s_enableServerAiaDownloads; internal static bool DisableTlsResume { get { - int disableTlsResume = s_disableTlsResume; - if (disableTlsResume != -1) + NullableBool disableTlsResume = s_disableTlsResume; + if (disableTlsResume != NullableBool.Undefined) { - return disableTlsResume != 0; + return disableTlsResume == NullableBool.True; } // First check for the AppContext switch, giving it priority over the environment variable. if (AppContext.TryGetSwitch(DisableTlsResumeCtxSwitch, out bool value)) { - s_disableTlsResume = value ? 1 : 0; + s_disableTlsResume = value ? NullableBool.True : NullableBool.False; } else { // AppContext switch wasn't used. Check the environment variable. s_disableTlsResume = Environment.GetEnvironmentVariable(DisableTlsResumeEnvironmentVariable) is string envVar && - (envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase)) ? 1 : 0; + (envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase)) ? NullableBool.True : NullableBool.False; } - return s_disableTlsResume != 0; + return s_disableTlsResume == NullableBool.True; } } @@ -55,26 +55,26 @@ internal static bool EnableServerAiaDownloads { get { - int enableServerAiaDownloads = s_enableServerAiaDownloads; - if (enableServerAiaDownloads != -1) + NullableBool enableServerAiaDownloads = s_enableServerAiaDownloads; + if (enableServerAiaDownloads != NullableBool.Undefined) { - return enableServerAiaDownloads != 0; + return enableServerAiaDownloads == NullableBool.True; } // First check for the AppContext switch, giving it priority over the environment variable. if (AppContext.TryGetSwitch(EnableServerAiaDownloadsCtxSwitch, out bool value)) { - s_enableServerAiaDownloads = value ? 1 : 0; + s_enableServerAiaDownloads = value ? NullableBool.True : NullableBool.False; } else { // AppContext switch wasn't used. Check the environment variable. s_enableServerAiaDownloads = Environment.GetEnvironmentVariable(EnableServerAiaDownloadsEnvironmentVariable) is string envVar && - (envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase)) ? 1 : 0; + (envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase)) ? NullableBool.True : NullableBool.False; } - return s_enableServerAiaDownloads != 0; + return s_enableServerAiaDownloads == NullableBool.True; } } diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs index 196ec738116e6a..cf6d2fa2ec34f1 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs @@ -500,12 +500,5 @@ internal long GetFileLength() FileStreamHelpers.CheckFileCall(result, Path); return status.Size; } - - private enum NullableBool - { - Undefined = 0, - False = -1, - True = 1 - } } } diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 798db989b0f12d..95c306717539b7 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1476,6 +1476,9 @@ Common\System\HexConverter.cs + + Common\System\NullableBool.cs + Common\System\HResults.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.cs index c351ff2d7f8668..5dd880078ca71d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.cs @@ -41,7 +41,7 @@ public readonly struct ProcessCpuUsage internal static bool IsSingleProcessor => RuntimeFeature.IsMultithreadingSupported ? ProcessorCount == 1 : true; public static int ProcessorCount { get; } = RuntimeFeature.IsMultithreadingSupported ? GetProcessorCount() : 1; - private static volatile sbyte s_privilegedProcess; + private static volatile NullableBool s_privilegedProcess; /// /// Gets whether the current process is authorized to perform security-relevant functions. @@ -50,12 +50,12 @@ public static bool IsPrivilegedProcess { get { - sbyte privilegedProcess = s_privilegedProcess; - if (privilegedProcess == 0) + NullableBool privilegedProcess = s_privilegedProcess; + if (privilegedProcess == NullableBool.Undefined) { - s_privilegedProcess = privilegedProcess = IsPrivilegedProcessCore() ? (sbyte)1 : (sbyte)-1; + s_privilegedProcess = privilegedProcess = IsPrivilegedProcessCore() ? NullableBool.True : NullableBool.False; } - return privilegedProcess > 0; + return privilegedProcess == NullableBool.True; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Icu.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Icu.cs index fc7fae4374b8f3..4d0e2c719d011f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Icu.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Icu.cs @@ -7,7 +7,7 @@ namespace System.Globalization { public partial class TextInfo { - private Tristate _needsTurkishCasing = Tristate.NotInitialized; + private NullableBool _needsTurkishCasing; private static bool NeedsTurkishCasing(string localeName) { @@ -27,11 +27,11 @@ internal unsafe void IcuChangeCase(char* src, int srcLen, char* dstBuffer, int d } else { - if (_needsTurkishCasing == Tristate.NotInitialized) + if (_needsTurkishCasing == NullableBool.Undefined) { - _needsTurkishCasing = NeedsTurkishCasing(_textInfoName) ? Tristate.True : Tristate.False; + _needsTurkishCasing = NeedsTurkishCasing(_textInfoName) ? NullableBool.True : NullableBool.False; } - if (_needsTurkishCasing == Tristate.True) + if (_needsTurkishCasing == NullableBool.True) { Interop.Globalization.ChangeCaseTurkish(src, srcLen, dstBuffer, dstBufferCapacity, bToUpper); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs index f9571bc811297c..9d0fd2a3e8b905 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs @@ -19,13 +19,6 @@ namespace System.Globalization /// public sealed partial class TextInfo : ICloneable, IDeserializationCallback { - private enum Tristate : byte - { - NotInitialized = 0, - False = 1, - True = 2 - } - private bool _isReadOnly; private readonly string _cultureName; @@ -36,10 +29,10 @@ private enum Tristate : byte // // Name of the text info we're using (ie: _cultureData.TextInfoName) private readonly string _textInfoName; - private Tristate _isAsciiCasingSameAsInvariant = Tristate.NotInitialized; + private NullableBool _isAsciiCasingSameAsInvariant; // Invariant text info - internal static readonly TextInfo Invariant = new TextInfo(CultureData.Invariant, readOnly: true) { _isAsciiCasingSameAsInvariant = Tristate.True }; + internal static readonly TextInfo Invariant = new TextInfo(CultureData.Invariant, readOnly: true) { _isAsciiCasingSameAsInvariant = NullableBool.True }; internal TextInfo(CultureData cultureData) { @@ -546,13 +539,13 @@ private bool IsAsciiCasingSameAsInvariant [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - if (_isAsciiCasingSameAsInvariant == Tristate.NotInitialized) + if (_isAsciiCasingSameAsInvariant == NullableBool.Undefined) { PopulateIsAsciiCasingSameAsInvariant(); } - Debug.Assert(_isAsciiCasingSameAsInvariant == Tristate.True || _isAsciiCasingSameAsInvariant == Tristate.False); - return _isAsciiCasingSameAsInvariant == Tristate.True; + Debug.Assert(_isAsciiCasingSameAsInvariant == NullableBool.True || _isAsciiCasingSameAsInvariant == NullableBool.False); + return _isAsciiCasingSameAsInvariant == NullableBool.True; } } @@ -560,7 +553,7 @@ private bool IsAsciiCasingSameAsInvariant private void PopulateIsAsciiCasingSameAsInvariant() { bool compareResult = CultureInfo.GetCultureInfo(_textInfoName).CompareInfo.Compare("abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", CompareOptions.IgnoreCase) == 0; - _isAsciiCasingSameAsInvariant = (compareResult) ? Tristate.True : Tristate.False; + _isAsciiCasingSameAsInvariant = compareResult ? NullableBool.True : NullableBool.False; } ///