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;
}
///