Add Interlocked unsigned and bitwise operations (#32216)
authorStephen Toub <stoub@microsoft.com>
Fri, 14 Feb 2020 17:12:31 +0000 (09:12 -0800)
committerGitHub <noreply@github.com>
Fri, 14 Feb 2020 17:12:31 +0000 (09:12 -0800)
* Add Interlocked unsigned and bitwise operations

* Address PR feedback

* Delete dead code for internal CompareExchange(..., ref bool)

* Remove Xor

18 files changed:
src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj
src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.sln
src/coreclr/src/System.Private.CoreLib/src/System/Threading/Interlocked.CoreCLR.cs [new file with mode: 0644]
src/coreclr/src/System.Private.CoreLib/src/System/Threading/Interlocked.cs [deleted file]
src/coreclr/src/vm/comutilnative.cpp
src/coreclr/src/vm/comutilnative.h
src/coreclr/src/vm/ecalllist.h
src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InterlockedBitVector32.cs
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueueSegment.cs
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs
src/libraries/System.Private.CoreLib/src/System/Threading/Interlocked.cs [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs
src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
src/libraries/System.Threading/ref/System.Threading.cs
src/libraries/System.Threading/tests/InterlockedTests.cs
src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj
src/mono/netcore/System.Private.CoreLib/src/System/Threading/Interlocked.Mono.cs [moved from src/mono/netcore/System.Private.CoreLib/src/System/Threading/Interlocked.cs with 99% similarity]

index 7bb8372..048a2eb 100644 (file)
     <Compile Include="$(BclSourcesRoot)\System\Threading\ClrThreadPoolBoundHandle.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\ClrThreadPoolBoundHandleOverlapped.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\ClrThreadPoolPreAllocatedOverlapped.cs" />
-    <Compile Include="$(BclSourcesRoot)\System\Threading\Interlocked.cs" />
+    <Compile Include="$(BclSourcesRoot)\System\Threading\Interlocked.CoreCLR.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\Monitor.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\Overlapped.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\StackCrawlMark.cs" />
index fad271a..86372c5 100644 (file)
@@ -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 (file)
index 0000000..3343714
--- /dev/null
@@ -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
+        /// <summary>Increments a specified variable and stores the result, as an atomic operation.</summary>
+        /// <param name="location">The variable whose value is to be incremented.</param>
+        /// <returns>The incremented value.</returns>
+        /// <exception cref="NullReferenceException">The address of location is a null pointer.</exception>
+        public static int Increment(ref int location) =>
+            Add(ref location, 1);
+
+        /// <summary>Increments a specified variable and stores the result, as an atomic operation.</summary>
+        /// <param name="location">The variable whose value is to be incremented.</param>
+        /// <returns>The incremented value.</returns>
+        /// <exception cref="NullReferenceException">The address of location is a null pointer.</exception>
+        public static long Increment(ref long location) =>
+            Add(ref location, 1);
+        #endregion
+
+        #region Decrement
+        /// <summary>Decrements a specified variable and stores the result, as an atomic operation.</summary>
+        /// <param name="location">The variable whose value is to be decremented.</param>
+        /// <returns>The decremented value.</returns>
+        /// <exception cref="NullReferenceException">The address of location is a null pointer.</exception>
+        public static int Decrement(ref int location) =>
+            Add(ref location, -1);
+
+        /// <summary>Decrements a specified variable and stores the result, as an atomic operation.</summary>
+        /// <param name="location">The variable whose value is to be decremented.</param>
+        /// <returns>The decremented value.</returns>
+        /// <exception cref="NullReferenceException">The address of location is a null pointer.</exception>
+        public static long Decrement(ref long location) =>
+            Add(ref location, -1);
+        #endregion
+
+        #region Exchange
+        /// <summary>Sets a 32-bit signed integer to a specified value and returns the original value, as an atomic operation.</summary>
+        /// <param name="location1">The variable to set to the specified value.</param>
+        /// <param name="value">The value to which the <paramref name="location1"/> parameter is set.</param>
+        /// <returns>The original value of <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of location1 is a null pointer.</exception>
+        [Intrinsic]
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern int Exchange(ref int location1, int value);
+
+        /// <summary>Sets a 64-bit signed integer to a specified value and returns the original value, as an atomic operation.</summary>
+        /// <param name="location1">The variable to set to the specified value.</param>
+        /// <param name="value">The value to which the <paramref name="location1"/> parameter is set.</param>
+        /// <returns>The original value of <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of location1 is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern long Exchange(ref long location1, long value);
+
+        /// <summary>Sets a single-precision floating point number to a specified value and returns the original value, as an atomic operation.</summary>
+        /// <param name="location1">The variable to set to the specified value.</param>
+        /// <param name="value">The value to which the <paramref name="location1"/> parameter is set.</param>
+        /// <returns>The original value of <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of location1 is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern float Exchange(ref float location1, float value);
+
+        /// <summary>Sets a double-precision floating point number to a specified value and returns the original value, as an atomic operation.</summary>
+        /// <param name="location1">The variable to set to the specified value.</param>
+        /// <param name="value">The value to which the <paramref name="location1"/> parameter is set.</param>
+        /// <returns>The original value of <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of location1 is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern double Exchange(ref double location1, double value);
+
+        /// <summary>Sets an object to the specified value and returns a reference to the original object, as an atomic operation.</summary>
+        /// <param name="location1">The variable to set to the specified value.</param>
+        /// <param name="value">The value to which the <paramref name="location1"/> parameter is set.</param>
+        /// <returns>The original value of <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of location1 is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        [return: NotNullIfNotNull("location1")]
+        public static extern object? Exchange([NotNullIfNotNull("value")] ref object? location1, object? value);
+
+        /// <summary>Sets a platform-specific handle or pointer to a specified value and returns the original value, as an atomic operation.</summary>
+        /// <param name="location1">The variable to set to the specified value.</param>
+        /// <param name="value">The value to which the <paramref name="location1"/> parameter is set.</param>
+        /// <returns>The original value of <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of location1 is a null pointer.</exception>
+        [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.
+
+        /// <summary>Sets a variable of the specified type <typeparamref name="T"/> to a specified value and returns the original value, as an atomic operation.</summary>
+        /// <param name="location1">The variable to set to the specified value.</param>
+        /// <param name="value">The value to which the <paramref name="location1"/> parameter is set.</param>
+        /// <returns>The original value of <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of location1 is a null pointer.</exception>
+        /// <typeparam name="T">The type to be used for <paramref name="location1"/> and <paramref name="value"/>. This type must be a reference type.</typeparam>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        [return: NotNullIfNotNull("location1")]
+        public static T Exchange<T>([NotNullIfNotNull("value")] ref T location1, T value) where T : class? =>
+            Unsafe.As<T>(Exchange(ref Unsafe.As<T, object?>(ref location1), value));
+        #endregion
+
+        #region CompareExchange
+        /// <summary>Compares two 32-bit signed integers for equality and, if they are equal, replaces the first value.</summary>
+        /// <param name="location1">The destination, whose value is compared with <paramref name="comparand"/> and possibly replaced.</param>
+        /// <param name="value">The value that replaces the destination value if the comparison results in equality.</param>
+        /// <param name="comparand">The value that is compared to the value at <paramref name="location1"/>.</param>
+        /// <returns>The original value in <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        [Intrinsic]
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern int CompareExchange(ref int location1, int value, int comparand);
+
+        /// <summary>Compares two 64-bit signed integers for equality and, if they are equal, replaces the first value.</summary>
+        /// <param name="location1">The destination, whose value is compared with <paramref name="comparand"/> and possibly replaced.</param>
+        /// <param name="value">The value that replaces the destination value if the comparison results in equality.</param>
+        /// <param name="comparand">The value that is compared to the value at <paramref name="location1"/>.</param>
+        /// <returns>The original value in <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern long CompareExchange(ref long location1, long value, long comparand);
+
+        /// <summary>Compares two single-precision floating point numbers for equality and, if they are equal, replaces the first value.</summary>
+        /// <param name="location1">The destination, whose value is compared with <paramref name="comparand"/> and possibly replaced.</param>
+        /// <param name="value">The value that replaces the destination value if the comparison results in equality.</param>
+        /// <param name="comparand">The value that is compared to the value at <paramref name="location1"/>.</param>
+        /// <returns>The original value in <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern float CompareExchange(ref float location1, float value, float comparand);
+
+        /// <summary>Compares two double-precision floating point numbers for equality and, if they are equal, replaces the first value.</summary>
+        /// <param name="location1">The destination, whose value is compared with <paramref name="comparand"/> and possibly replaced.</param>
+        /// <param name="value">The value that replaces the destination value if the comparison results in equality.</param>
+        /// <param name="comparand">The value that is compared to the value at <paramref name="location1"/>.</param>
+        /// <returns>The original value in <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern double CompareExchange(ref double location1, double value, double comparand);
+
+        /// <summary>Compares two objects for reference equality and, if they are equal, replaces the first object.</summary>
+        /// <param name="location1">The destination object that is compared by reference with <paramref name="comparand"/> and possibly replaced.</param>
+        /// <param name="value">The object that replaces the destination object if the reference comparison results in equality.</param>
+        /// <param name="comparand">The object that is compared by reference to the object at <paramref name="location1"/>.</param>
+        /// <returns>The original value in <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        [return: NotNullIfNotNull("location1")]
+        public static extern object? CompareExchange(ref object? location1, object? value, object? comparand);
+
+        /// <summary>Compares two platform-specific handles or pointers for equality and, if they are equal, replaces the first one.</summary>
+        /// <param name="location1">The destination <see cref="IntPtr"/>, whose value is compared with the value of <paramref name="comparand"/> and possibly replaced by <paramref name="value"/>.</param>
+        /// <param name="value">The <see cref="IntPtr"/> that replaces the destination value if the comparison results in equality.</param>
+        /// <param name="comparand">The <see cref="IntPtr"/> that is compared to the value at <paramref name="location1"/>.</param>
+        /// <returns>The original value in <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        [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.
+
+        /// <summary>Compares two instances of the specified reference type <typeparamref name="T"/> for reference equality and, if they are equal, replaces the first one.</summary>
+        /// <param name="location1">The destination, whose value is compared by reference with <paramref name="comparand"/> and possibly replaced.</param>
+        /// <param name="value">The value that replaces the destination value if the comparison by reference results in equality.</param>
+        /// <param name="comparand">The object that is compared by reference to the value at <paramref name="location1"/>.</param>
+        /// <returns>The original value in <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        /// <typeparam name="T">The type to be used for <paramref name="location1"/>, <paramref name="value"/>, and <paramref name="comparand"/>. This type must be a reference type.</typeparam>
+        [return: NotNullIfNotNull("location1")]
+        [Intrinsic]
+        public static T CompareExchange<T>(ref T location1, T value, T comparand) where T : class? =>
+            Unsafe.As<T>(CompareExchange(ref Unsafe.As<T, object?>(ref location1), value, comparand));
+        #endregion
+
+        #region Add
+        /// <summary>Adds two 32-bit signed integers and replaces the first integer with the sum, as an atomic operation.</summary>
+        /// <param name="location1">A variable containing the first value to be added. The sum of the two values is stored in <paramref name="location1"/>.</param>
+        /// <param name="value">The value to be added to the integer at <paramref name="location1"/>.</param>
+        /// <returns>The new value stored at <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        public static int Add(ref int location1, int value) =>
+            ExchangeAdd(ref location1, value) + value;
+
+        /// <summary>Adds two 64-bit signed integers and replaces the first integer with the sum, as an atomic operation.</summary>
+        /// <param name="location1">A variable containing the first value to be added. The sum of the two values is stored in <paramref name="location1"/>.</param>
+        /// <param name="value">The value to be added to the integer at <paramref name="location1"/>.</param>
+        /// <returns>The new value stored at <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        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
+        /// <summary>Returns a 64-bit signed value, loaded as an atomic operation.</summary>
+        /// <param name="location">The 64-bit value to be loaded.</param>
+        /// <returns>The loaded value.</returns>
+        public static long Read(ref long location) =>
+            CompareExchange(ref location, 0, 0);
+        #endregion
+
+        #region MemoryBarrier
+        /// <summary>
+        /// 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 <see cref="MemoryBarrier"/> execute after memory accesses that follow the call to <see cref="MemoryBarrier"/>.
+        /// </summary>
+        [Intrinsic]
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern void MemoryBarrier();
+
+        [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
+        private static extern void _MemoryBarrierProcessWide();
+
+        /// <summary>Provides a process-wide memory barrier that ensures that reads and writes from any CPU cannot move across the barrier.</summary>
+        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 (file)
index 40c261b..0000000
+++ /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
-{
-    /// <summary>
-    /// 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.
-    /// </summary>
-    public static class Interlocked
-    {
-        /// <summary>
-        /// Implemented: int, long
-        /// </summary>
-        public static int Increment(ref int location)
-        {
-            return Add(ref location, 1);
-        }
-
-        public static long Increment(ref long location)
-        {
-            return Add(ref location, 1);
-        }
-
-        /// <summary>
-        /// Implemented: int, long
-        /// </summary>
-        public static int Decrement(ref int location)
-        {
-            return Add(ref location, -1);
-        }
-
-        public static long Decrement(ref long location)
-        {
-            return Add(ref location, -1);
-        }
-
-        /// <summary>
-        /// Implemented: int, long, float, double, Object, IntPtr
-        /// </summary>
-        [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<T>([NotNullIfNotNull("value")] ref T location1, T value) where T : class?
-        {
-            return Unsafe.As<T>(Exchange(ref Unsafe.As<T, object?>(ref location1), value));
-        }
-
-        /// <summary>
-        /// Implemented: int, long, float, double, Object, IntPtr
-        /// </summary>
-        [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<T>(ref T location1, T value, T comparand) where T : class?
-        {
-            return Unsafe.As<T>(CompareExchange(ref Unsafe.As<T, object?>(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);
-
-        /// <summary>
-        /// Implemented: int, long
-        /// </summary>
-        [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();
-        }
-    }
-}
index 1b31fff..7d0dea1 100644 (file)
@@ -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;
index 17442d9..56030c4 100644 (file)
@@ -172,7 +172,6 @@ public:
         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);
index 4f1b50c..8190151 100644 (file)
@@ -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)
index 815cc96..11cd3a0 100644 (file)
@@ -20,14 +20,13 @@ namespace System.ComponentModel
             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);
                 }
             }
         }
index 4c42870..7c678c2 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\ExecutionContext.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\IOCompletionCallback.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\IThreadPoolWorkItem.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Interlocked.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\LazyInitializer.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\LazyThreadSafetyMode.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\LockRecursionException.cs" />
index e9525b4..8787287 100644 (file)
@@ -112,18 +112,7 @@ namespace System.Collections.Concurrent
             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);
             }
         }
 
index 053a70f..6e04dcb 100644 (file)
@@ -101,15 +101,8 @@ namespace System.Runtime.InteropServices
         {
             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 (file)
index 0000000..16bb9bb
--- /dev/null
@@ -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
+{
+    /// <summary>Provides atomic operations for variables that are shared by multiple threads.</summary>
+    public static partial class Interlocked
+    {
+        #region Increment
+        /// <summary>Increments a specified variable and stores the result, as an atomic operation.</summary>
+        /// <param name="location">The variable whose value is to be incremented.</param>
+        /// <returns>The incremented value.</returns>
+        /// <exception cref="NullReferenceException">The address of location is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        [CLSCompliant(false)]
+        public static uint Increment(ref uint location) =>
+            Add(ref location, 1);
+
+        /// <summary>Increments a specified variable and stores the result, as an atomic operation.</summary>
+        /// <param name="location">The variable whose value is to be incremented.</param>
+        /// <returns>The incremented value.</returns>
+        /// <exception cref="NullReferenceException">The address of location is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        [CLSCompliant(false)]
+        public static ulong Increment(ref ulong location) =>
+            Add(ref location, 1);
+        #endregion
+
+        #region Decrement
+        /// <summary>Decrements a specified variable and stores the result, as an atomic operation.</summary>
+        /// <param name="location">The variable whose value is to be decremented.</param>
+        /// <returns>The decremented value.</returns>
+        /// <exception cref="NullReferenceException">The address of location is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        [CLSCompliant(false)]
+        public static uint Decrement(ref uint location) =>
+            (uint)Add(ref Unsafe.As<uint, int>(ref location), -1);
+
+        /// <summary>Decrements a specified variable and stores the result, as an atomic operation.</summary>
+        /// <param name="location">The variable whose value is to be decremented.</param>
+        /// <returns>The decremented value.</returns>
+        /// <exception cref="NullReferenceException">The address of location is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        [CLSCompliant(false)]
+        public static ulong Decrement(ref ulong location) =>
+            (ulong)Add(ref Unsafe.As<ulong, long>(ref location), -1);
+        #endregion
+
+        #region Exchange
+        /// <summary>Sets a 32-bit unsigned integer to a specified value and returns the original value, as an atomic operation.</summary>
+        /// <param name="location1">The variable to set to the specified value.</param>
+        /// <param name="value">The value to which the <paramref name="location1"/> parameter is set.</param>
+        /// <returns>The original value of <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of location1 is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        [CLSCompliant(false)]
+        public static uint Exchange(ref uint location1, uint value) =>
+            (uint)Exchange(ref Unsafe.As<uint, int>(ref location1), (int)value);
+
+        /// <summary>Sets a 64-bit unsigned integer to a specified value and returns the original value, as an atomic operation.</summary>
+        /// <param name="location1">The variable to set to the specified value.</param>
+        /// <param name="value">The value to which the <paramref name="location1"/> parameter is set.</param>
+        /// <returns>The original value of <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of location1 is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        [CLSCompliant(false)]
+        public static ulong Exchange(ref ulong location1, ulong value) =>
+            (ulong)Exchange(ref Unsafe.As<ulong, long>(ref location1), (long)value);
+        #endregion
+
+        #region CompareExchange
+        /// <summary>Compares two 32-bit unsigned integers for equality and, if they are equal, replaces the first value.</summary>
+        /// <param name="location1">The destination, whose value is compared with <paramref name="comparand"/> and possibly replaced.</param>
+        /// <param name="value">The value that replaces the destination value if the comparison results in equality.</param>
+        /// <param name="comparand">The value that is compared to the value at <paramref name="location1"/>.</param>
+        /// <returns>The original value in <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        [CLSCompliant(false)]
+        public static uint CompareExchange(ref uint location1, uint value, uint comparand) =>
+            (uint)CompareExchange(ref Unsafe.As<uint, int>(ref location1), (int)value, (int)comparand);
+
+        /// <summary>Compares two 64-bit unsigned integers for equality and, if they are equal, replaces the first value.</summary>
+        /// <param name="location1">The destination, whose value is compared with <paramref name="comparand"/> and possibly replaced.</param>
+        /// <param name="value">The value that replaces the destination value if the comparison results in equality.</param>
+        /// <param name="comparand">The value that is compared to the value at <paramref name="location1"/>.</param>
+        /// <returns>The original value in <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        [CLSCompliant(false)]
+        public static ulong CompareExchange(ref ulong location1, ulong value, ulong comparand) =>
+            (ulong)CompareExchange(ref Unsafe.As<ulong, long>(ref location1), (long)value, (long)comparand);
+        #endregion
+
+        #region Add
+        /// <summary>Adds two 32-bit unsigned integers and replaces the first integer with the sum, as an atomic operation.</summary>
+        /// <param name="location1">A variable containing the first value to be added. The sum of the two values is stored in <paramref name="location1"/>.</param>
+        /// <param name="value">The value to be added to the integer at <paramref name="location1"/>.</param>
+        /// <returns>The new value stored at <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        [CLSCompliant(false)]
+        public static uint Add(ref uint location1, uint value) =>
+            (uint)Add(ref Unsafe.As<uint, int>(ref location1), (int)value);
+
+        /// <summary>Adds two 64-bit unsigned integers and replaces the first integer with the sum, as an atomic operation.</summary>
+        /// <param name="location1">A variable containing the first value to be added. The sum of the two values is stored in <paramref name="location1"/>.</param>
+        /// <param name="value">The value to be added to the integer at <paramref name="location1"/>.</param>
+        /// <returns>The new value stored at <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        [CLSCompliant(false)]
+        public static ulong Add(ref ulong location1, ulong value) =>
+            (ulong)Add(ref Unsafe.As<ulong, long>(ref location1), (long)value);
+        #endregion
+
+        #region Read
+        /// <summary>Returns a 64-bit unsigned value, loaded as an atomic operation.</summary>
+        /// <param name="location">The 64-bit value to be loaded.</param>
+        /// <returns>The loaded value.</returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        [CLSCompliant(false)]
+        public static ulong Read(ref ulong location) =>
+            CompareExchange(ref location, 0, 0);
+        #endregion
+
+        #region And
+        /// <summary>Bitwise "ands" two 32-bit signed integers and replaces the first integer with the result, as an atomic operation.</summary>
+        /// <param name="location1">A variable containing the first value to be combined. The result is stored in <paramref name="location1"/>.</param>
+        /// <param name="value">The value to be combined with the integer at <paramref name="location1"/>.</param>
+        /// <returns>The new value stored at <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        [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;
+            }
+        }
+
+        /// <summary>Bitwise "ands" two 32-bit unsigned integers and replaces the first integer with the result, as an atomic operation.</summary>
+        /// <param name="location1">A variable containing the first value to be combined. The result is stored in <paramref name="location1"/>.</param>
+        /// <param name="value">The value to be combined with the integer at <paramref name="location1"/>.</param>
+        /// <returns>The new value stored at <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        [CLSCompliant(false)]
+        public static uint And(ref uint location1, uint value) =>
+            (uint)And(ref Unsafe.As<uint, int>(ref location1), (int)value);
+
+        /// <summary>Bitwise "ands" two 64-bit signed integers and replaces the first integer with the result, as an atomic operation.</summary>
+        /// <param name="location1">A variable containing the first value to be combined. The result is stored in <paramref name="location1"/>.</param>
+        /// <param name="value">The value to be combined with the integer at <paramref name="location1"/>.</param>
+        /// <returns>The new value stored at <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        [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;
+            }
+        }
+
+        /// <summary>Bitwise "ands" two 64-bit unsigned integers and replaces the first integer with the result, as an atomic operation.</summary>
+        /// <param name="location1">A variable containing the first value to be combined. The result is stored in <paramref name="location1"/>.</param>
+        /// <param name="value">The value to be combined with the integer at <paramref name="location1"/>.</param>
+        /// <returns>The new value stored at <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        [CLSCompliant(false)]
+        public static ulong And(ref ulong location1, ulong value) =>
+            (ulong)And(ref Unsafe.As<ulong, long>(ref location1), (long)value);
+        #endregion
+
+        #region Or
+        /// <summary>Bitwise "ors" two 32-bit signed integers and replaces the first integer with the result, as an atomic operation.</summary>
+        /// <param name="location1">A variable containing the first value to be combined. The result is stored in <paramref name="location1"/>.</param>
+        /// <param name="value">The value to be combined with the integer at <paramref name="location1"/>.</param>
+        /// <returns>The new value stored at <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        [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;
+            }
+        }
+
+        /// <summary>Bitwise "ors" two 32-bit unsigned integers and replaces the first integer with the result, as an atomic operation.</summary>
+        /// <param name="location1">A variable containing the first value to be combined. The result is stored in <paramref name="location1"/>.</param>
+        /// <param name="value">The value to be combined with the integer at <paramref name="location1"/>.</param>
+        /// <returns>The new value stored at <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        [CLSCompliant(false)]
+        public static uint Or(ref uint location1, uint value) =>
+            (uint)Or(ref Unsafe.As<uint, int>(ref location1), (int)value);
+
+        /// <summary>Bitwise "ors" two 64-bit signed integers and replaces the first integer with the result, as an atomic operation.</summary>
+        /// <param name="location1">A variable containing the first value to be combined. The result is stored in <paramref name="location1"/>.</param>
+        /// <param name="value">The value to be combined with the integer at <paramref name="location1"/>.</param>
+        /// <returns>The new value stored at <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        [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;
+            }
+        }
+
+        /// <summary>Bitwise "ors" two 64-bit unsigned integers and replaces the first integer with the result, as an atomic operation.</summary>
+        /// <param name="location1">A variable containing the first value to be combined. The result is stored in <paramref name="location1"/>.</param>
+        /// <param name="value">The value to be combined with the integer at <paramref name="location1"/>.</param>
+        /// <returns>The new value stored at <paramref name="location1"/>.</returns>
+        /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        [CLSCompliant(false)]
+        public static ulong Or(ref ulong location1, ulong value) =>
+            (ulong)Or(ref Unsafe.As<ulong, long>(ref location1), (long)value);
+        #endregion
+    }
+}
index 8ce8004..38960e6 100644 (file)
@@ -729,21 +729,15 @@ namespace System.Threading.Tasks
 
             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);
             }
         }
 
index 4e174e8..5d75557 100644 (file)
@@ -1110,24 +1110,13 @@ namespace System.Text.RegularExpressions
                 // 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;
index 93c3fa2..4c8dabc 100644 (file)
@@ -152,10 +152,24 @@ namespace System.Threading
     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 @@ namespace System.Threading
         [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("location1")]
         public static T CompareExchange<T>(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 @@ namespace System.Threading
         [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("location1")]
         public static T Exchange<T>([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
     {
index fffb397..b879bed 100644 (file)
@@ -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<Task> threads = new List<Task>();
             int count = 0;
             for (int i = 0; i < 10000; i++)
@@ -27,8 +111,20 @@ namespace System.Threading.Tests
         }
 
         [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<Task> threads = new List<Task>();
             long count = 0;
             for (int i = 0; i < 10000; i++)
@@ -40,6 +136,172 @@ namespace System.Threading.Tests
             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()
         {
index 45e60e8..dcfbd0b 100644 (file)
       <Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\AssemblyDependencyResolver.cs" />
       <Compile Include="$(BclSourcesRoot)\System\Runtime\Remoting\Contexts\Context.cs" />
       <Compile Include="$(BclSourcesRoot)\System\Security\DynamicSecurityMethodAttribute.cs" />
-      <Compile Include="$(BclSourcesRoot)\System\Threading\Interlocked.cs" />
+      <Compile Include="$(BclSourcesRoot)\System\Threading\Interlocked.Mono.cs" />
       <Compile Include="$(BclSourcesRoot)\System\Threading\Monitor.cs" />
       <Compile Include="$(BclSourcesRoot)\System\Threading\Overlapped.cs" />
       <Compile Include="$(BclSourcesRoot)\System\Threading\PreAllocatedOverlapped.cs" />
@@ -8,7 +8,7 @@ using System.Diagnostics.CodeAnalysis;
 
 namespace System.Threading
 {
-       public static class Interlocked
+       public static partial class Interlocked
        {
                [Intrinsic]
                [MethodImplAttribute (MethodImplOptions.InternalCall)]