From 4107a4cf5dda28c815f78dc962e0d1e7286676cc Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 14 Feb 2020 09:12:31 -0800 Subject: [PATCH] Add Interlocked unsigned and bitwise operations (#32216) * Add Interlocked unsigned and bitwise operations * Address PR feedback * Delete dead code for internal CompareExchange(..., ref bool) * Remove Xor --- .../System.Private.CoreLib.csproj | 2 +- .../System.Private.CoreLib.sln | 2 +- .../System/Threading/Interlocked.CoreCLR.cs | 242 ++++++++++++++++ .../src/System/Threading/Interlocked.cs | 159 ----------- src/coreclr/src/vm/comutilnative.cpp | 16 -- src/coreclr/src/vm/comutilnative.h | 1 - src/coreclr/src/vm/ecalllist.h | 1 - .../ComponentModel/InterlockedBitVector32.cs | 13 +- .../System.Private.CoreLib.Shared.projitems | 1 + .../Concurrent/ConcurrentQueueSegment.cs | 13 +- .../Runtime/InteropServices/SafeHandle.cs | 11 +- .../src/System/Threading/Interlocked.cs | 259 +++++++++++++++++ .../src/System/Threading/Tasks/Task.cs | 12 +- .../Text/RegularExpressions/RegexCharClass.cs | 15 +- .../System.Threading/ref/System.Threading.cs | 34 +++ .../tests/InterlockedTests.cs | 266 +++++++++++++++++- .../System.Private.CoreLib.csproj | 2 +- .../{Interlocked.cs => Interlocked.Mono.cs} | 2 +- 18 files changed, 818 insertions(+), 233 deletions(-) create mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/Threading/Interlocked.CoreCLR.cs delete mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/Threading/Interlocked.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/Interlocked.cs rename src/mono/netcore/System.Private.CoreLib/src/System/Threading/{Interlocked.cs => Interlocked.Mono.cs} (99%) 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 7bb83729b44d8..048a2eba87938 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 fad271abc0be3..86372c56c8a4d 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 0000000000000..33437141e7612 --- /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 40c261b6f757d..0000000000000 --- 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 1b31fff645917..7d0dea14da052 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 17442d91431c9..56030c4382a81 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 4f1b50c12058a..8190151176651 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 815cc96f112b2..11cd3a0cf71fc 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 4c42870c7db34..7c678c27002b8 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 e9525b47b8254..8787287e6fd13 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 053a70f156674..6e04dcba3c715 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 0000000000000..16bb9bbf4e256 --- /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 8ce80046baf03..38960e6f6bf16 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 4e174e8088f01..5d75557ab0d0f 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 93c3fa25c2723..4c8dabcf6c621 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 fffb397fa7f09..b879bed40b8d4 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 45e60e8e87130..dcfbd0b809ebf 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 cbc041c7d3537..ff25f260c86e3 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)]