diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj
index 7bb83729b44d8c..048a2eba87938d 100644
--- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -267,7 +267,7 @@
-
+
diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.sln b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.sln
index fad271abc0be3e..86372c56c8a4d6 100644
--- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.sln
+++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.sln
@@ -1,4 +1,3 @@
-
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28902.138
@@ -9,6 +8,7 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "System.Private.CoreLib.Shar
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
+ ..\..\..\libraries\System.Private.CoreLib\src\System.Private.CoreLib.Shared.projitems*{3da06c3a-2e7b-4cb7-80ed-9b12916013f9}*SharedItemsImports = 5
..\..\..\libraries\System.Private.CoreLib\src\System.Private.CoreLib.Shared.projitems*{845c8b26-350b-4e63-bd11-2c8150444e28}*SharedItemsImports = 13
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Threading/Interlocked.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Threading/Interlocked.CoreCLR.cs
new file mode 100644
index 00000000000000..33437141e76121
--- /dev/null
+++ b/src/coreclr/src/System.Private.CoreLib/src/System/Threading/Interlocked.CoreCLR.cs
@@ -0,0 +1,242 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Internal.Runtime.CompilerServices;
+
+namespace System.Threading
+{
+ public static partial class Interlocked
+ {
+ #region Increment
+ /// Increments a specified variable and stores the result, as an atomic operation.
+ /// The variable whose value is to be incremented.
+ /// The incremented value.
+ /// The address of location is a null pointer.
+ public static int Increment(ref int location) =>
+ Add(ref location, 1);
+
+ /// Increments a specified variable and stores the result, as an atomic operation.
+ /// The variable whose value is to be incremented.
+ /// The incremented value.
+ /// The address of location is a null pointer.
+ public static long Increment(ref long location) =>
+ Add(ref location, 1);
+ #endregion
+
+ #region Decrement
+ /// Decrements a specified variable and stores the result, as an atomic operation.
+ /// The variable whose value is to be decremented.
+ /// The decremented value.
+ /// The address of location is a null pointer.
+ public static int Decrement(ref int location) =>
+ Add(ref location, -1);
+
+ /// Decrements a specified variable and stores the result, as an atomic operation.
+ /// The variable whose value is to be decremented.
+ /// The decremented value.
+ /// The address of location is a null pointer.
+ public static long Decrement(ref long location) =>
+ Add(ref location, -1);
+ #endregion
+
+ #region Exchange
+ /// Sets a 32-bit signed integer to a specified value and returns the original value, as an atomic operation.
+ /// The variable to set to the specified value.
+ /// The value to which the parameter is set.
+ /// The original value of .
+ /// The address of location1 is a null pointer.
+ [Intrinsic]
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern int Exchange(ref int location1, int value);
+
+ /// Sets a 64-bit signed integer to a specified value and returns the original value, as an atomic operation.
+ /// The variable to set to the specified value.
+ /// The value to which the parameter is set.
+ /// The original value of .
+ /// The address of location1 is a null pointer.
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern long Exchange(ref long location1, long value);
+
+ /// Sets a single-precision floating point number to a specified value and returns the original value, as an atomic operation.
+ /// The variable to set to the specified value.
+ /// The value to which the parameter is set.
+ /// The original value of .
+ /// The address of location1 is a null pointer.
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern float Exchange(ref float location1, float value);
+
+ /// Sets a double-precision floating point number to a specified value and returns the original value, as an atomic operation.
+ /// The variable to set to the specified value.
+ /// The value to which the parameter is set.
+ /// The original value of .
+ /// The address of location1 is a null pointer.
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern double Exchange(ref double location1, double value);
+
+ /// Sets an object to the specified value and returns a reference to the original object, as an atomic operation.
+ /// The variable to set to the specified value.
+ /// The value to which the parameter is set.
+ /// The original value of .
+ /// The address of location1 is a null pointer.
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ [return: NotNullIfNotNull("location1")]
+ public static extern object? Exchange([NotNullIfNotNull("value")] ref object? location1, object? value);
+
+ /// Sets a platform-specific handle or pointer to a specified value and returns the original value, as an atomic operation.
+ /// The variable to set to the specified value.
+ /// The value to which the parameter is set.
+ /// The original value of .
+ /// The address of location1 is a null pointer.
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern IntPtr Exchange(ref IntPtr location1, IntPtr value);
+
+ // The below whole method reduces to a single call to Exchange(ref object, object) but
+ // the JIT thinks that it will generate more native code than it actually does.
+
+ /// Sets a variable of the specified type to a specified value and returns the original value, as an atomic operation.
+ /// The variable to set to the specified value.
+ /// The value to which the parameter is set.
+ /// The original value of .
+ /// The address of location1 is a null pointer.
+ /// The type to be used for and . This type must be a reference type.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [return: NotNullIfNotNull("location1")]
+ public static T Exchange([NotNullIfNotNull("value")] ref T location1, T value) where T : class? =>
+ Unsafe.As(Exchange(ref Unsafe.As(ref location1), value));
+ #endregion
+
+ #region CompareExchange
+ /// Compares two 32-bit signed integers for equality and, if they are equal, replaces the first value.
+ /// The destination, whose value is compared with and possibly replaced.
+ /// The value that replaces the destination value if the comparison results in equality.
+ /// The value that is compared to the value at .
+ /// The original value in .
+ /// The address of is a null pointer.
+ [Intrinsic]
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern int CompareExchange(ref int location1, int value, int comparand);
+
+ /// Compares two 64-bit signed integers for equality and, if they are equal, replaces the first value.
+ /// The destination, whose value is compared with and possibly replaced.
+ /// The value that replaces the destination value if the comparison results in equality.
+ /// The value that is compared to the value at .
+ /// The original value in .
+ /// The address of is a null pointer.
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern long CompareExchange(ref long location1, long value, long comparand);
+
+ /// Compares two single-precision floating point numbers for equality and, if they are equal, replaces the first value.
+ /// The destination, whose value is compared with and possibly replaced.
+ /// The value that replaces the destination value if the comparison results in equality.
+ /// The value that is compared to the value at .
+ /// The original value in .
+ /// The address of is a null pointer.
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern float CompareExchange(ref float location1, float value, float comparand);
+
+ /// Compares two double-precision floating point numbers for equality and, if they are equal, replaces the first value.
+ /// The destination, whose value is compared with and possibly replaced.
+ /// The value that replaces the destination value if the comparison results in equality.
+ /// The value that is compared to the value at .
+ /// The original value in .
+ /// The address of is a null pointer.
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern double CompareExchange(ref double location1, double value, double comparand);
+
+ /// Compares two objects for reference equality and, if they are equal, replaces the first object.
+ /// The destination object that is compared by reference with and possibly replaced.
+ /// The object that replaces the destination object if the reference comparison results in equality.
+ /// The object that is compared by reference to the object at .
+ /// The original value in .
+ /// The address of is a null pointer.
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ [return: NotNullIfNotNull("location1")]
+ public static extern object? CompareExchange(ref object? location1, object? value, object? comparand);
+
+ /// Compares two platform-specific handles or pointers for equality and, if they are equal, replaces the first one.
+ /// The destination , whose value is compared with the value of and possibly replaced by .
+ /// The that replaces the destination value if the comparison results in equality.
+ /// The that is compared to the value at .
+ /// The original value in .
+ /// The address of is a null pointer.
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern IntPtr CompareExchange(ref IntPtr location1, IntPtr value, IntPtr comparand);
+
+ // Note that getILIntrinsicImplementationForInterlocked() in vm\jitinterface.cpp replaces
+ // the body of the following method with the the following IL:
+ // ldarg.0
+ // ldarg.1
+ // ldarg.2
+ // call System.Threading.Interlocked::CompareExchange(ref Object, Object, Object)
+ // ret
+ // The workaround is no longer strictly necessary now that we have Unsafe.As but it does
+ // have the advantage of being less sensitive to JIT's inliner decisions.
+
+ /// Compares two instances of the specified reference type for reference equality and, if they are equal, replaces the first one.
+ /// The destination, whose value is compared by reference with and possibly replaced.
+ /// The value that replaces the destination value if the comparison by reference results in equality.
+ /// The object that is compared by reference to the value at .
+ /// The original value in .
+ /// The address of is a null pointer.
+ /// The type to be used for , , and . This type must be a reference type.
+ [return: NotNullIfNotNull("location1")]
+ [Intrinsic]
+ public static T CompareExchange(ref T location1, T value, T comparand) where T : class? =>
+ Unsafe.As(CompareExchange(ref Unsafe.As(ref location1), value, comparand));
+ #endregion
+
+ #region Add
+ /// Adds two 32-bit signed integers and replaces the first integer with the sum, as an atomic operation.
+ /// A variable containing the first value to be added. The sum of the two values is stored in .
+ /// The value to be added to the integer at .
+ /// The new value stored at .
+ /// The address of is a null pointer.
+ public static int Add(ref int location1, int value) =>
+ ExchangeAdd(ref location1, value) + value;
+
+ /// Adds two 64-bit signed integers and replaces the first integer with the sum, as an atomic operation.
+ /// A variable containing the first value to be added. The sum of the two values is stored in .
+ /// The value to be added to the integer at .
+ /// The new value stored at .
+ /// The address of is a null pointer.
+ public static long Add(ref long location1, long value) =>
+ ExchangeAdd(ref location1, value) + value;
+
+ [Intrinsic]
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern int ExchangeAdd(ref int location1, int value);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern long ExchangeAdd(ref long location1, long value);
+ #endregion
+
+ #region Read
+ /// Returns a 64-bit signed value, loaded as an atomic operation.
+ /// The 64-bit value to be loaded.
+ /// The loaded value.
+ public static long Read(ref long location) =>
+ CompareExchange(ref location, 0, 0);
+ #endregion
+
+ #region MemoryBarrier
+ ///
+ /// Synchronizes memory access as follows:
+ /// The processor that executes the current thread cannot reorder instructions in such a way that memory accesses before
+ /// the call to execute after memory accesses that follow the call to .
+ ///
+ [Intrinsic]
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern void MemoryBarrier();
+
+ [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
+ private static extern void _MemoryBarrierProcessWide();
+
+ /// Provides a process-wide memory barrier that ensures that reads and writes from any CPU cannot move across the barrier.
+ public static void MemoryBarrierProcessWide() => _MemoryBarrierProcessWide();
+ #endregion
+ }
+}
diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Threading/Interlocked.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Threading/Interlocked.cs
deleted file mode 100644
index 40c261b6f757d1..00000000000000
--- a/src/coreclr/src/System.Private.CoreLib/src/System/Threading/Interlocked.cs
+++ /dev/null
@@ -1,159 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Runtime.CompilerServices;
-using Internal.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Diagnostics.CodeAnalysis;
-
-namespace System.Threading
-{
- ///
- /// After much discussion, we decided the Interlocked class doesn't need
- /// any HPA's for synchronization or external threading. They hurt C#'s
- /// codegen for the yield keyword, and arguably they didn't protect much.
- /// Instead, they penalized people (and compilers) for writing threadsafe
- /// code.
- ///
- public static class Interlocked
- {
- ///
- /// Implemented: int, long
- ///
- public static int Increment(ref int location)
- {
- return Add(ref location, 1);
- }
-
- public static long Increment(ref long location)
- {
- return Add(ref location, 1);
- }
-
- ///
- /// Implemented: int, long
- ///
- public static int Decrement(ref int location)
- {
- return Add(ref location, -1);
- }
-
- public static long Decrement(ref long location)
- {
- return Add(ref location, -1);
- }
-
- ///
- /// Implemented: int, long, float, double, Object, IntPtr
- ///
- [Intrinsic]
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern int Exchange(ref int location1, int value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern long Exchange(ref long location1, long value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern float Exchange(ref float location1, float value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern double Exchange(ref double location1, double value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- [return: NotNullIfNotNull("location1")]
- public static extern object? Exchange([NotNullIfNotNull("value")] ref object? location1, object? value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern IntPtr Exchange(ref IntPtr location1, IntPtr value);
-
- // This whole method reduces to a single call to Exchange(ref object, object) but
- // the JIT thinks that it will generate more native code than it actually does.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- [return: NotNullIfNotNull("location1")]
- public static T Exchange([NotNullIfNotNull("value")] ref T location1, T value) where T : class?
- {
- return Unsafe.As(Exchange(ref Unsafe.As(ref location1), value));
- }
-
- ///
- /// Implemented: int, long, float, double, Object, IntPtr
- ///
- [Intrinsic]
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern int CompareExchange(ref int location1, int value, int comparand);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern long CompareExchange(ref long location1, long value, long comparand);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern float CompareExchange(ref float location1, float value, float comparand);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern double CompareExchange(ref double location1, double value, double comparand);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- [return: NotNullIfNotNull("location1")]
- public static extern object? CompareExchange(ref object? location1, object? value, object? comparand);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern IntPtr CompareExchange(ref IntPtr location1, IntPtr value, IntPtr comparand);
-
- // Note that getILIntrinsicImplementationForInterlocked() in vm\jitinterface.cpp replaces
- // the body of this method with the the following IL:
- // ldarg.0
- // ldarg.1
- // ldarg.2
- // call System.Threading.Interlocked::CompareExchange(ref Object, Object, Object)
- // ret
- // The workaround is no longer strictly necessary now that we have Unsafe.As but it does
- // have the advantage of being less sensitive to JIT's inliner decisions.
- [return: NotNullIfNotNull("location1")]
- [Intrinsic]
- public static T CompareExchange(ref T location1, T value, T comparand) where T : class?
- {
- return Unsafe.As(CompareExchange(ref Unsafe.As(ref location1), value, comparand));
- }
-
- // BCL-internal overload that returns success via a ref bool param, useful for reliable spin locks.
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int CompareExchange(ref int location1, int value, int comparand, ref bool succeeded);
-
- ///
- /// Implemented: int, long
- ///
- [Intrinsic]
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int ExchangeAdd(ref int location1, int value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern long ExchangeAdd(ref long location1, long value);
-
- public static int Add(ref int location1, int value)
- {
- return ExchangeAdd(ref location1, value) + value;
- }
-
- public static long Add(ref long location1, long value)
- {
- return ExchangeAdd(ref location1, value) + value;
- }
-
- public static long Read(ref long location)
- {
- return Interlocked.CompareExchange(ref location, 0, 0);
- }
-
- [Intrinsic]
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern void MemoryBarrier();
-
- [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
- private static extern void _MemoryBarrierProcessWide();
-
- public static void MemoryBarrierProcessWide()
- {
- _MemoryBarrierProcessWide();
- }
- }
-}
diff --git a/src/coreclr/src/vm/comutilnative.cpp b/src/coreclr/src/vm/comutilnative.cpp
index 1b31fff6459179..7d0dea14da052c 100644
--- a/src/coreclr/src/vm/comutilnative.cpp
+++ b/src/coreclr/src/vm/comutilnative.cpp
@@ -1648,22 +1648,6 @@ FCIMPL3(INT32, COMInterlocked::CompareExchange, INT32* location, INT32 value, IN
}
FCIMPLEND
-FCIMPL4(INT32, COMInterlocked::CompareExchangeReliableResult, INT32* location, INT32 value, INT32 comparand, CLR_BOOL* succeeded)
-{
- FCALL_CONTRACT;
-
- if( NULL == location) {
- FCThrow(kNullReferenceException);
- }
-
- INT32 result = FastInterlockCompareExchange((LONG*)location, value, comparand);
- if (result == comparand)
- *succeeded = true;
-
- return result;
-}
-FCIMPLEND
-
FCIMPL3_IVV(INT64, COMInterlocked::CompareExchange64, INT64* location, INT64 value, INT64 comparand)
{
FCALL_CONTRACT;
diff --git a/src/coreclr/src/vm/comutilnative.h b/src/coreclr/src/vm/comutilnative.h
index 17442d91431c96..56030c4382a81b 100644
--- a/src/coreclr/src/vm/comutilnative.h
+++ b/src/coreclr/src/vm/comutilnative.h
@@ -172,7 +172,6 @@ class COMInterlocked
static FCDECL2_IV(INT64, Exchange64, INT64 *location, INT64 value);
static FCDECL2(LPVOID, ExchangePointer, LPVOID* location, LPVOID value);
static FCDECL3(INT32, CompareExchange, INT32* location, INT32 value, INT32 comparand);
- static FCDECL4(INT32, CompareExchangeReliableResult, INT32* location, INT32 value, INT32 comparand, CLR_BOOL* succeeded);
static FCDECL3_IVV(INT64, CompareExchange64, INT64* location, INT64 value, INT64 comparand);
static FCDECL3(LPVOID, CompareExchangePointer, LPVOID* location, LPVOID value, LPVOID comparand);
static FCDECL2_IV(float, ExchangeFloat, float *location, float value);
diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h
index 4f1b50c12058a1..8190151176651a 100644
--- a/src/coreclr/src/vm/ecalllist.h
+++ b/src/coreclr/src/vm/ecalllist.h
@@ -869,7 +869,6 @@ FCFuncStart(gInterlockedFuncs)
FCFuncElementSig("CompareExchange", &gsig_SM_RefDbl_Dbl_Dbl_RetDbl, COMInterlocked::CompareExchangeDouble)
FCFuncElementSig("CompareExchange", &gsig_SM_RefFlt_Flt_Flt_RetFlt, COMInterlocked::CompareExchangeFloat)
FCFuncElementSig("CompareExchange", &gsig_SM_RefObj_Obj_Obj_RetObj, COMInterlocked::CompareExchangeObject)
- FCFuncElementSig("CompareExchange", &gsig_SM_RefInt_Int_Int_RefBool_RetInt, COMInterlocked::CompareExchangeReliableResult)
FCFuncElementSig("CompareExchange", &gsig_SM_RefIntPtr_IntPtr_IntPtr_RetIntPtr, COMInterlocked::CompareExchangePointer)
FCIntrinsicSig("ExchangeAdd", &gsig_SM_RefInt_Int_RetInt, COMInterlocked::ExchangeAdd32, CORINFO_INTRINSIC_InterlockedXAdd32)
FCIntrinsicSig("ExchangeAdd", &gsig_SM_RefLong_Long_RetLong, COMInterlocked::ExchangeAdd64, CORINFO_INTRINSIC_InterlockedXAdd64)
diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InterlockedBitVector32.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InterlockedBitVector32.cs
index 815cc96f112b22..11cd3a0cf71fc4 100644
--- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InterlockedBitVector32.cs
+++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InterlockedBitVector32.cs
@@ -20,14 +20,13 @@ public bool this[int bit]
get => (Volatile.Read(ref _data) & bit) == bit;
set
{
- while (true)
+ if (value)
{
- int oldValue = _data;
- int newValue = value ? oldValue | bit : oldValue &= ~bit;
- if (Interlocked.CompareExchange(ref _data, newValue, oldValue) == oldValue)
- {
- break;
- }
+ Interlocked.Or(ref _data, bit);
+ }
+ else
+ {
+ Interlocked.And(ref _data, ~bit);
}
}
}
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 4c42870c7db34f..7c678c27002b88 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
@@ -848,6 +848,7 @@
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueueSegment.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueueSegment.cs
index e9525b47b8254d..8787287e6fd132 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueueSegment.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueueSegment.cs
@@ -112,18 +112,7 @@ internal static int RoundUpToPowerOf2(int i)
if (!_frozenForEnqueues) // flag used to ensure we don't increase the Tail more than once if frozen more than once
{
_frozenForEnqueues = true;
-
- // Increase the tail by FreezeOffset, spinning until we're successful in doing so.
- int tail = _headAndTail.Tail;
- while (true)
- {
- int oldTail = Interlocked.CompareExchange(ref _headAndTail.Tail, tail + FreezeOffset, tail);
- if (oldTail == tail)
- {
- break;
- }
- tail = oldTail;
- }
+ Interlocked.Add(ref _headAndTail.Tail, FreezeOffset);
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs
index 053a70f156674b..6e04dcba3c7151 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs
@@ -101,15 +101,8 @@ public void SetHandleAsInvalid()
{
Debug.Assert(_fullyInitialized);
- // Attempt to set closed state (low order bit of the _state field).
- // Might have to attempt these repeatedly, if the operation suffers
- // interference from an AddRef or Release.
- int oldState, newState;
- do
- {
- oldState = _state;
- newState = oldState | StateBits.Closed;
- } while (Interlocked.CompareExchange(ref _state, newState, oldState) != oldState);
+ // Set closed state (low order bit of the _state field).
+ Interlocked.Or(ref _state, StateBits.Closed);
GC.SuppressFinalize(this);
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Interlocked.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Interlocked.cs
new file mode 100644
index 00000000000000..16bb9bbf4e256f
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Interlocked.cs
@@ -0,0 +1,259 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.CompilerServices;
+using Internal.Runtime.CompilerServices;
+
+namespace System.Threading
+{
+ /// Provides atomic operations for variables that are shared by multiple threads.
+ public static partial class Interlocked
+ {
+ #region Increment
+ /// Increments a specified variable and stores the result, as an atomic operation.
+ /// The variable whose value is to be incremented.
+ /// The incremented value.
+ /// The address of location is a null pointer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [CLSCompliant(false)]
+ public static uint Increment(ref uint location) =>
+ Add(ref location, 1);
+
+ /// Increments a specified variable and stores the result, as an atomic operation.
+ /// The variable whose value is to be incremented.
+ /// The incremented value.
+ /// The address of location is a null pointer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [CLSCompliant(false)]
+ public static ulong Increment(ref ulong location) =>
+ Add(ref location, 1);
+ #endregion
+
+ #region Decrement
+ /// Decrements a specified variable and stores the result, as an atomic operation.
+ /// The variable whose value is to be decremented.
+ /// The decremented value.
+ /// The address of location is a null pointer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [CLSCompliant(false)]
+ public static uint Decrement(ref uint location) =>
+ (uint)Add(ref Unsafe.As(ref location), -1);
+
+ /// Decrements a specified variable and stores the result, as an atomic operation.
+ /// The variable whose value is to be decremented.
+ /// The decremented value.
+ /// The address of location is a null pointer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [CLSCompliant(false)]
+ public static ulong Decrement(ref ulong location) =>
+ (ulong)Add(ref Unsafe.As(ref location), -1);
+ #endregion
+
+ #region Exchange
+ /// Sets a 32-bit unsigned integer to a specified value and returns the original value, as an atomic operation.
+ /// The variable to set to the specified value.
+ /// The value to which the parameter is set.
+ /// The original value of .
+ /// The address of location1 is a null pointer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [CLSCompliant(false)]
+ public static uint Exchange(ref uint location1, uint value) =>
+ (uint)Exchange(ref Unsafe.As(ref location1), (int)value);
+
+ /// Sets a 64-bit unsigned integer to a specified value and returns the original value, as an atomic operation.
+ /// The variable to set to the specified value.
+ /// The value to which the parameter is set.
+ /// The original value of .
+ /// The address of location1 is a null pointer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [CLSCompliant(false)]
+ public static ulong Exchange(ref ulong location1, ulong value) =>
+ (ulong)Exchange(ref Unsafe.As(ref location1), (long)value);
+ #endregion
+
+ #region CompareExchange
+ /// Compares two 32-bit unsigned integers for equality and, if they are equal, replaces the first value.
+ /// The destination, whose value is compared with and possibly replaced.
+ /// The value that replaces the destination value if the comparison results in equality.
+ /// The value that is compared to the value at .
+ /// The original value in .
+ /// The address of is a null pointer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [CLSCompliant(false)]
+ public static uint CompareExchange(ref uint location1, uint value, uint comparand) =>
+ (uint)CompareExchange(ref Unsafe.As(ref location1), (int)value, (int)comparand);
+
+ /// Compares two 64-bit unsigned integers for equality and, if they are equal, replaces the first value.
+ /// The destination, whose value is compared with and possibly replaced.
+ /// The value that replaces the destination value if the comparison results in equality.
+ /// The value that is compared to the value at .
+ /// The original value in .
+ /// The address of is a null pointer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [CLSCompliant(false)]
+ public static ulong CompareExchange(ref ulong location1, ulong value, ulong comparand) =>
+ (ulong)CompareExchange(ref Unsafe.As(ref location1), (long)value, (long)comparand);
+ #endregion
+
+ #region Add
+ /// Adds two 32-bit unsigned integers and replaces the first integer with the sum, as an atomic operation.
+ /// A variable containing the first value to be added. The sum of the two values is stored in .
+ /// The value to be added to the integer at .
+ /// The new value stored at .
+ /// The address of is a null pointer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [CLSCompliant(false)]
+ public static uint Add(ref uint location1, uint value) =>
+ (uint)Add(ref Unsafe.As(ref location1), (int)value);
+
+ /// Adds two 64-bit unsigned integers and replaces the first integer with the sum, as an atomic operation.
+ /// A variable containing the first value to be added. The sum of the two values is stored in .
+ /// The value to be added to the integer at .
+ /// The new value stored at .
+ /// The address of is a null pointer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [CLSCompliant(false)]
+ public static ulong Add(ref ulong location1, ulong value) =>
+ (ulong)Add(ref Unsafe.As(ref location1), (long)value);
+ #endregion
+
+ #region Read
+ /// Returns a 64-bit unsigned value, loaded as an atomic operation.
+ /// The 64-bit value to be loaded.
+ /// The loaded value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [CLSCompliant(false)]
+ public static ulong Read(ref ulong location) =>
+ CompareExchange(ref location, 0, 0);
+ #endregion
+
+ #region And
+ /// Bitwise "ands" two 32-bit signed integers and replaces the first integer with the result, as an atomic operation.
+ /// A variable containing the first value to be combined. The result is stored in .
+ /// The value to be combined with the integer at .
+ /// The new value stored at .
+ /// The address of is a null pointer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int And(ref int location1, int value)
+ {
+ int current = location1;
+ while (true)
+ {
+ int newValue = current & value;
+ int oldValue = CompareExchange(ref location1, newValue, current);
+ if (oldValue == current)
+ {
+ return newValue;
+ }
+ current = oldValue;
+ }
+ }
+
+ /// Bitwise "ands" two 32-bit unsigned integers and replaces the first integer with the result, as an atomic operation.
+ /// A variable containing the first value to be combined. The result is stored in .
+ /// The value to be combined with the integer at .
+ /// The new value stored at .
+ /// The address of is a null pointer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [CLSCompliant(false)]
+ public static uint And(ref uint location1, uint value) =>
+ (uint)And(ref Unsafe.As(ref location1), (int)value);
+
+ /// Bitwise "ands" two 64-bit signed integers and replaces the first integer with the result, as an atomic operation.
+ /// A variable containing the first value to be combined. The result is stored in .
+ /// The value to be combined with the integer at .
+ /// The new value stored at .
+ /// The address of is a null pointer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static long And(ref long location1, long value)
+ {
+ long current = location1;
+ while (true)
+ {
+ long newValue = current & value;
+ long oldValue = CompareExchange(ref location1, newValue, current);
+ if (oldValue == current)
+ {
+ return newValue;
+ }
+ current = oldValue;
+ }
+ }
+
+ /// Bitwise "ands" two 64-bit unsigned integers and replaces the first integer with the result, as an atomic operation.
+ /// A variable containing the first value to be combined. The result is stored in .
+ /// The value to be combined with the integer at .
+ /// The new value stored at .
+ /// The address of is a null pointer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [CLSCompliant(false)]
+ public static ulong And(ref ulong location1, ulong value) =>
+ (ulong)And(ref Unsafe.As(ref location1), (long)value);
+ #endregion
+
+ #region Or
+ /// Bitwise "ors" two 32-bit signed integers and replaces the first integer with the result, as an atomic operation.
+ /// A variable containing the first value to be combined. The result is stored in .
+ /// The value to be combined with the integer at .
+ /// The new value stored at .
+ /// The address of is a null pointer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int Or(ref int location1, int value)
+ {
+ int current = location1;
+ while (true)
+ {
+ int newValue = current | value;
+ int oldValue = CompareExchange(ref location1, newValue, current);
+ if (oldValue == current)
+ {
+ return newValue;
+ }
+ current = oldValue;
+ }
+ }
+
+ /// Bitwise "ors" two 32-bit unsigned integers and replaces the first integer with the result, as an atomic operation.
+ /// A variable containing the first value to be combined. The result is stored in .
+ /// The value to be combined with the integer at .
+ /// The new value stored at .
+ /// The address of is a null pointer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [CLSCompliant(false)]
+ public static uint Or(ref uint location1, uint value) =>
+ (uint)Or(ref Unsafe.As(ref location1), (int)value);
+
+ /// Bitwise "ors" two 64-bit signed integers and replaces the first integer with the result, as an atomic operation.
+ /// A variable containing the first value to be combined. The result is stored in .
+ /// The value to be combined with the integer at .
+ /// The new value stored at .
+ /// The address of is a null pointer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static long Or(ref long location1, long value)
+ {
+ long current = location1;
+ while (true)
+ {
+ long newValue = current | value;
+ long oldValue = CompareExchange(ref location1, newValue, current);
+ if (oldValue == current)
+ {
+ return newValue;
+ }
+ current = oldValue;
+ }
+ }
+
+ /// Bitwise "ors" two 64-bit unsigned integers and replaces the first integer with the result, as an atomic operation.
+ /// A variable containing the first value to be combined. The result is stored in .
+ /// The value to be combined with the integer at .
+ /// The new value stored at .
+ /// The address of is a null pointer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [CLSCompliant(false)]
+ public static ulong Or(ref ulong location1, ulong value) =>
+ (ulong)Or(ref Unsafe.As(ref location1), (long)value);
+ #endregion
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs
index 8ce80046baf035..38960e6f6bf166 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs
@@ -729,21 +729,15 @@ internal void SetNotificationForWaitCompletion(bool enabled)
if (enabled)
{
- // Atomically set the END_AWAIT_NOTIFICATION bit
+ // Atomically set the TASK_STATE_WAIT_COMPLETION_NOTIFICATION bit
bool success = AtomicStateUpdate(TASK_STATE_WAIT_COMPLETION_NOTIFICATION,
TASK_STATE_COMPLETED_MASK | TASK_STATE_COMPLETION_RESERVED);
Debug.Assert(success, "Tried to set enabled on completed Task");
}
else
{
- // Atomically clear the END_AWAIT_NOTIFICATION bit
- int flags = m_stateFlags;
- while (true)
- {
- int oldFlags = Interlocked.CompareExchange(ref m_stateFlags, flags & (~TASK_STATE_WAIT_COMPLETION_NOTIFICATION), flags);
- if (oldFlags == flags) break;
- flags = oldFlags;
- }
+ // Atomically clear the TASK_STATE_WAIT_COMPLETION_NOTIFICATION bit
+ Interlocked.And(ref m_stateFlags, ~TASK_STATE_WAIT_COMPLETION_NOTIFICATION);
}
}
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
index 4e174e8088f015..5d75557ab0d0fe 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
@@ -1110,24 +1110,13 @@ public static bool CharInClass(char ch, string set, ref int[]? asciiResultCache)
// Otherwise, compute it normally.
bool isInClass = CharInClass(ch, set);
- // Determine which bits to write back to the array.
+ // Determine which bits to write back to the array and "or" the bits back in a thread-safe manner.
int bitsToSet = knownBit;
if (isInClass)
{
bitsToSet |= valueBit;
}
-
- // "or" the bits back in a thread-safe manner.
- while (true)
- {
- int oldValue = Interlocked.CompareExchange(ref slot, current | bitsToSet, current);
- if (oldValue == current)
- {
- break;
- }
-
- current = oldValue;
- }
+ Interlocked.Or(ref slot, bitsToSet);
// Return the computed value.
return isInClass;
diff --git a/src/libraries/System.Threading/ref/System.Threading.cs b/src/libraries/System.Threading/ref/System.Threading.cs
index b809a48f1be3b5..a4cda25e10fa98 100644
--- a/src/libraries/System.Threading/ref/System.Threading.cs
+++ b/src/libraries/System.Threading/ref/System.Threading.cs
@@ -152,10 +152,24 @@ public virtual void Revert(object previousState) { }
public static partial class Interlocked
{
public static int Add(ref int location1, int value) { throw null; }
+ [System.CLSCompliant(false)]
+ public static uint Add(ref uint location1, uint value) { throw null; }
public static long Add(ref long location1, long value) { throw null; }
+ [System.CLSCompliant(false)]
+ public static ulong Add(ref ulong location1, ulong value) { throw null; }
+ public static int And(ref int location1, int value) { throw null; }
+ [System.CLSCompliant(false)]
+ public static uint And(ref uint location1, uint value) { throw null; }
+ public static long And(ref long location1, long value) { throw null; }
+ [System.CLSCompliant(false)]
+ public static ulong And(ref ulong location1, ulong value) { throw null; }
public static double CompareExchange(ref double location1, double value, double comparand) { throw null; }
public static int CompareExchange(ref int location1, int value, int comparand) { throw null; }
+ [System.CLSCompliant(false)]
+ public static uint CompareExchange(ref uint location1, uint value, uint comparand) { throw null; }
public static long CompareExchange(ref long location1, long value, long comparand) { throw null; }
+ [System.CLSCompliant(false)]
+ public static ulong CompareExchange(ref ulong location1, ulong value, ulong comparand) { throw null; }
public static System.IntPtr CompareExchange(ref System.IntPtr location1, System.IntPtr value, System.IntPtr comparand) { throw null; }
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("location1")]
public static object? CompareExchange(ref object? location1, object? value, object? comparand) { throw null; }
@@ -163,10 +177,18 @@ public static partial class Interlocked
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("location1")]
public static T CompareExchange(ref T location1, T value, T comparand) where T : class? { throw null; }
public static int Decrement(ref int location) { throw null; }
+ [System.CLSCompliant(false)]
+ public static uint Decrement(ref uint location) { throw null; }
public static long Decrement(ref long location) { throw null; }
+ [System.CLSCompliant(false)]
+ public static ulong Decrement(ref ulong location) { throw null; }
public static double Exchange(ref double location1, double value) { throw null; }
public static int Exchange(ref int location1, int value) { throw null; }
+ [System.CLSCompliant(false)]
+ public static uint Exchange(ref uint location1, uint value) { throw null; }
public static long Exchange(ref long location1, long value) { throw null; }
+ [System.CLSCompliant(false)]
+ public static ulong Exchange(ref ulong location1, ulong value) { throw null; }
public static System.IntPtr Exchange(ref System.IntPtr location1, System.IntPtr value) { throw null; }
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("location1")]
public static object? Exchange([System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("value")] ref object? location1, object? value) { throw null; }
@@ -174,10 +196,22 @@ public static partial class Interlocked
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("location1")]
public static T Exchange([System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("value")] ref T location1, T value) where T : class? { throw null; }
public static int Increment(ref int location) { throw null; }
+ [System.CLSCompliant(false)]
+ public static uint Increment(ref uint location) { throw null; }
public static long Increment(ref long location) { throw null; }
+ [System.CLSCompliant(false)]
+ public static ulong Increment(ref ulong location) { throw null; }
public static void MemoryBarrier() { }
public static void MemoryBarrierProcessWide() { }
+ public static int Or(ref int location1, int value) { throw null; }
+ [System.CLSCompliant(false)]
+ public static uint Or(ref uint location1, uint value) { throw null; }
+ public static long Or(ref long location1, long value) { throw null; }
+ [System.CLSCompliant(false)]
+ public static ulong Or(ref ulong location1, ulong value) { throw null; }
public static long Read(ref long location) { throw null; }
+ [System.CLSCompliant(false)]
+ public static ulong Read(ref ulong location) { throw null; }
}
public static partial class LazyInitializer
{
diff --git a/src/libraries/System.Threading/tests/InterlockedTests.cs b/src/libraries/System.Threading/tests/InterlockedTests.cs
index fffb397fa7f098..b879bed40b8d49 100644
--- a/src/libraries/System.Threading/tests/InterlockedTests.cs
+++ b/src/libraries/System.Threading/tests/InterlockedTests.cs
@@ -13,8 +13,92 @@ namespace System.Threading.Tests
public class InterlockedTests
{
[Fact]
- public void IncrementDecrement_int()
+ public void InterlockedAdd_Int32()
{
+ int value = 42;
+ Assert.Equal(12387, Interlocked.Add(ref value, 12345));
+ Assert.Equal(12387, Interlocked.Add(ref value, 0));
+ Assert.Equal(12386, Interlocked.Add(ref value, -1));
+
+ value = int.MaxValue;
+ Assert.Equal(int.MinValue, Interlocked.Add(ref value, 1));
+ }
+
+ [Fact]
+ public void InterlockedAdd_UInt32()
+ {
+ uint value = 42;
+ Assert.Equal(12387u, Interlocked.Add(ref value, 12345u));
+ Assert.Equal(12387u, Interlocked.Add(ref value, 0u));
+ Assert.Equal(9386u, Interlocked.Add(ref value, 4294964295u));
+
+ value = uint.MaxValue;
+ Assert.Equal(0u, Interlocked.Add(ref value, 1));
+ }
+
+ [Fact]
+ public void InterlockedAdd_Int64()
+ {
+ long value = 42;
+ Assert.Equal(12387, Interlocked.Add(ref value, 12345));
+ Assert.Equal(12387, Interlocked.Add(ref value, 0));
+ Assert.Equal(12386, Interlocked.Add(ref value, -1));
+
+ value = long.MaxValue;
+ Assert.Equal(long.MinValue, Interlocked.Add(ref value, 1));
+ }
+
+ [Fact]
+ public void InterlockedAdd_UInt64()
+ {
+ ulong value = 42;
+ Assert.Equal(12387u, Interlocked.Add(ref value, 12345));
+ Assert.Equal(12387u, Interlocked.Add(ref value, 0));
+ Assert.Equal(10771u, Interlocked.Add(ref value, 18446744073709550000));
+
+ value = ulong.MaxValue;
+ Assert.Equal(0u, Interlocked.Add(ref value, 1));
+ }
+
+ [Fact]
+ public void InterlockedIncrement_Int32()
+ {
+ int value = 42;
+ Assert.Equal(43, Interlocked.Increment(ref value));
+ Assert.Equal(43, value);
+ }
+
+ [Fact]
+ public void InterlockedIncrement_UInt32()
+ {
+ uint value = 42u;
+ Assert.Equal(43u, Interlocked.Increment(ref value));
+ Assert.Equal(43u, value);
+ }
+
+ [Fact]
+ public void InterlockedIncrement_Int64()
+ {
+ long value = 42;
+ Assert.Equal(43, Interlocked.Increment(ref value));
+ Assert.Equal(43, value);
+ }
+
+ [Fact]
+ public void InterlockedIncrement_UInt64()
+ {
+ ulong value = 42u;
+ Assert.Equal(43u, Interlocked.Increment(ref value));
+ Assert.Equal(43u, value);
+ }
+
+ [Fact]
+ public void InterlockedDecrement_Int32()
+ {
+ int value = 42;
+ Assert.Equal(41, Interlocked.Decrement(ref value));
+ Assert.Equal(41, value);
+
List threads = new List();
int count = 0;
for (int i = 0; i < 10000; i++)
@@ -27,8 +111,20 @@ public void IncrementDecrement_int()
}
[Fact]
- public void IncrementDecrement_long()
+ public void InterlockedDecrement_UInt32()
+ {
+ uint value = 42u;
+ Assert.Equal(41u, Interlocked.Decrement(ref value));
+ Assert.Equal(41u, value);
+ }
+
+ [Fact]
+ public void InterlockedDecrement_Int64()
{
+ long value = 42;
+ Assert.Equal(41, Interlocked.Decrement(ref value));
+ Assert.Equal(41, value);
+
List threads = new List();
long count = 0;
for (int i = 0; i < 10000; i++)
@@ -40,6 +136,172 @@ public void IncrementDecrement_long()
Assert.Equal(0, count);
}
+ [Fact]
+ public void InterlockedDecrement_UInt64()
+ {
+ ulong value = 42u;
+ Assert.Equal(41u, Interlocked.Decrement(ref value));
+ Assert.Equal(41u, value);
+ }
+
+ [Fact]
+ public void InterlockedExchange_Int32()
+ {
+ int value = 42;
+ Assert.Equal(42, Interlocked.Exchange(ref value, 12345));
+ Assert.Equal(12345, value);
+ }
+
+ [Fact]
+ public void InterlockedExchange_UInt32()
+ {
+ uint value = 42;
+ Assert.Equal(42u, Interlocked.Exchange(ref value, 12345u));
+ Assert.Equal(12345u, value);
+ }
+
+ [Fact]
+ public void InterlockedExchange_Int64()
+ {
+ long value = 42;
+ Assert.Equal(42, Interlocked.Exchange(ref value, 12345));
+ Assert.Equal(12345, value);
+ }
+
+ [Fact]
+ public void InterlockedExchange_UInt64()
+ {
+ ulong value = 42;
+ Assert.Equal(42u, Interlocked.Exchange(ref value, 12345u));
+ Assert.Equal(12345u, value);
+ }
+
+ [Fact]
+ public void InterlockedCompareExchange_Int32()
+ {
+ int value = 42;
+
+ Assert.Equal(42, Interlocked.CompareExchange(ref value, 12345, 41));
+ Assert.Equal(42, value);
+
+ Assert.Equal(42, Interlocked.CompareExchange(ref value, 12345, 42));
+ Assert.Equal(12345, value);
+ }
+
+ [Fact]
+ public void InterlockedCompareExchange_UInt32()
+ {
+ uint value = 42;
+
+ Assert.Equal(42u, Interlocked.CompareExchange(ref value, 12345u, 41u));
+ Assert.Equal(42u, value);
+
+ Assert.Equal(42u, Interlocked.CompareExchange(ref value, 12345u, 42u));
+ Assert.Equal(12345u, value);
+ }
+
+ [Fact]
+ public void InterlockedCompareExchange_Int64()
+ {
+ long value = 42;
+
+ Assert.Equal(42, Interlocked.CompareExchange(ref value, 12345, 41));
+ Assert.Equal(42, value);
+
+ Assert.Equal(42, Interlocked.CompareExchange(ref value, 12345, 42));
+ Assert.Equal(12345, value);
+ }
+
+ [Fact]
+ public void InterlockedCompareExchange_UInt64()
+ {
+ ulong value = 42;
+
+ Assert.Equal(42u, Interlocked.CompareExchange(ref value, 12345u, 41u));
+ Assert.Equal(42u, value);
+
+ Assert.Equal(42u, Interlocked.CompareExchange(ref value, 12345u, 42u));
+ Assert.Equal(12345u, value);
+ }
+
+ [Fact]
+ public void InterlockedRead_Int64()
+ {
+ long value = long.MaxValue - 42;
+ Assert.Equal(long.MaxValue - 42, Interlocked.Read(ref value));
+ }
+
+ [Fact]
+ public void InterlockedRead_UInt64()
+ {
+ ulong value = ulong.MaxValue - 42;
+ Assert.Equal(ulong.MaxValue - 42, Interlocked.Read(ref value));
+ }
+
+ [Fact]
+ public void InterlockedAnd_Int32()
+ {
+ int value = 0x12345670;
+ Assert.Equal(0x02244220, Interlocked.And(ref value, 0x7654321));
+ Assert.Equal(0x02244220, value);
+ }
+
+ [Fact]
+ public void InterlockedAnd_UInt32()
+ {
+ uint value = 0x12345670u;
+ Assert.Equal(0x02244220u, Interlocked.And(ref value, 0x7654321));
+ Assert.Equal(0x02244220u, value);
+ }
+
+ [Fact]
+ public void InterlockedAnd_Int64()
+ {
+ long value = 0x12345670;
+ Assert.Equal(0x02244220, Interlocked.And(ref value, 0x7654321));
+ Assert.Equal(0x02244220, value);
+ }
+
+ [Fact]
+ public void InterlockedAnd_UInt64()
+ {
+ ulong value = 0x12345670u;
+ Assert.Equal(0x02244220u, Interlocked.And(ref value, 0x7654321));
+ Assert.Equal(0x02244220u, value);
+ }
+
+ [Fact]
+ public void InterlockedOr_Int32()
+ {
+ int value = 0x12345670;
+ Assert.Equal(0x17755771, Interlocked.Or(ref value, 0x7654321));
+ Assert.Equal(0x17755771, value);
+ }
+
+ [Fact]
+ public void InterlockedOr_UInt32()
+ {
+ uint value = 0x12345670u;
+ Assert.Equal(0x17755771u, Interlocked.Or(ref value, 0x7654321));
+ Assert.Equal(0x17755771u, value);
+ }
+
+ [Fact]
+ public void InterlockedOr_Int64()
+ {
+ long value = 0x12345670;
+ Assert.Equal(0x17755771, Interlocked.Or(ref value, 0x7654321));
+ Assert.Equal(0x17755771, value);
+ }
+
+ [Fact]
+ public void InterlockedOr_UInt64()
+ {
+ ulong value = 0x12345670u;
+ Assert.Equal(0x17755771u, Interlocked.Or(ref value, 0x7654321));
+ Assert.Equal(0x17755771u, value);
+ }
+
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/coreclr/issues/20215")]
public void MemoryBarrierProcessWide()
{
diff --git a/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj
index 45e60e8e871309..dcfbd0b809ebf3 100644
--- a/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -253,7 +253,7 @@
-
+
diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/Threading/Interlocked.cs b/src/mono/netcore/System.Private.CoreLib/src/System/Threading/Interlocked.Mono.cs
similarity index 99%
rename from src/mono/netcore/System.Private.CoreLib/src/System/Threading/Interlocked.cs
rename to src/mono/netcore/System.Private.CoreLib/src/System/Threading/Interlocked.Mono.cs
index cbc041c7d35371..ff25f260c86e33 100644
--- a/src/mono/netcore/System.Private.CoreLib/src/System/Threading/Interlocked.cs
+++ b/src/mono/netcore/System.Private.CoreLib/src/System/Threading/Interlocked.Mono.cs
@@ -8,7 +8,7 @@
namespace System.Threading
{
- public static class Interlocked
+ public static partial class Interlocked
{
[Intrinsic]
[MethodImplAttribute (MethodImplOptions.InternalCall)]