// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; 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 8-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.AggressiveInlining)] [CLSCompliant(false)] public static sbyte Exchange(ref sbyte location1, sbyte value) => (sbyte)Exchange(ref Unsafe.As<sbyte, byte>(ref location1), (byte)value); /// <summary>Sets a 16-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> [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short Exchange(ref short location1, short value) => (short)Exchange(ref Unsafe.As<short, ushort>(ref location1), (ushort)value); /// <summary>Sets a 8-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> [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte Exchange(ref byte location1, byte value) { #if (MONO && (TARGET_AMD64 || TARGET_ARM64 || TARGET_WASM)) || (!MONO && (TARGET_X86 || TARGET_AMD64 || TARGET_ARM64)) return Exchange(ref location1, value); // Must expand intrinsic #else // this relies on GC keeping 4B alignment for refs and on subtracting to such alignment being in the same object nuint offset = Unsafe.OpportunisticMisalignment(ref location1, sizeof(uint)); ref uint alignedRef = ref Unsafe.As<byte, uint>(ref Unsafe.SubtractByteOffset(ref location1, offset)); int bitOffset = (int)((BitConverter.IsLittleEndian ? offset : sizeof(uint) - offset - sizeof(byte)) * 8); // to bit offset Debug.Assert(bitOffset is 0 or 8 or 16 or 24); uint mask = ~((uint)byte.MaxValue << bitOffset); uint shiftedValue = (uint)value << bitOffset; // this doesn't need to be volatile since CompareExchange will update stale values uint originalValue = alignedRef; uint newValue; do { // make sure the ref is still aligned Debug.Assert(Unsafe.IsOpportunisticallyAligned(ref alignedRef, sizeof(uint))); newValue = originalValue & mask | shiftedValue; } while (originalValue != (originalValue = CompareExchange(ref alignedRef, newValue, originalValue))); // verify the GC hasn't broken the ref Debug.Assert((nuint)Unsafe.ByteOffset(ref Unsafe.As<uint, byte>(ref alignedRef), ref location1) == offset); return (byte)(originalValue >> bitOffset); #endif } /// <summary>Sets a 16-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.AggressiveInlining)] [CLSCompliant(false)] public static ushort Exchange(ref ushort location1, ushort value) { #if ((MONO && (TARGET_AMD64 || TARGET_ARM64 || TARGET_WASM)) || !MONO && (TARGET_X86 || TARGET_AMD64 || TARGET_ARM64)) return Exchange(ref location1, value); // Must expand intrinsic #else // this relies on GC keeping 4B alignment for refs and on subtracting to such alignment being in the same object nuint offset = Unsafe.OpportunisticMisalignment(ref location1, sizeof(uint)); ref uint alignedRef = ref Unsafe.As<ushort, uint>(ref Unsafe.SubtractByteOffset(ref location1, offset)); int bitOffset = (int)((BitConverter.IsLittleEndian ? offset : sizeof(uint) - offset - sizeof(ushort)) * 8); // to bit offset Debug.Assert(bitOffset is 0 or 16); uint mask = ~((uint)ushort.MaxValue << bitOffset); uint shiftedValue = (uint)value << bitOffset; // this doesn't need to be volatile since CompareExchange will update stale values uint originalValue = alignedRef; uint newValue; do { // make sure the ref is still aligned Debug.Assert(Unsafe.IsOpportunisticallyAligned(ref alignedRef, sizeof(uint))); newValue = originalValue & mask | shiftedValue; } while (originalValue != (originalValue = CompareExchange(ref alignedRef, newValue, originalValue))); // verify the GC hasn't broken the ref Debug.Assert((nuint)Unsafe.ByteOffset(ref Unsafe.As<uint, ushort>(ref alignedRef), ref location1) == offset); return (ushort)(originalValue >> bitOffset); #endif } /// <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> [Intrinsic] [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> [Intrinsic] [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); /// <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.AggressiveInlining)] public static float Exchange(ref float location1, float value) => Unsafe.BitCast<int, float>(Exchange(ref Unsafe.As<float, int>(ref location1), Unsafe.BitCast<float, int>(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.AggressiveInlining)] public static double Exchange(ref double location1, double value) => Unsafe.BitCast<long, double>(Exchange(ref Unsafe.As<double, long>(ref location1), Unsafe.BitCast<double, long>(value))); /// <summary>Sets a native-sized 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.AggressiveInlining)] public static nint Exchange(ref nint location1, nint value) { #if TARGET_64BIT return (nint)Exchange(ref Unsafe.As<nint, long>(ref location1), (long)value); #else return (nint)Exchange(ref Unsafe.As<nint, int>(ref location1), (int)value); #endif } /// <summary>Sets a native-sized 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> [Intrinsic] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static nuint Exchange(ref nuint location1, nuint value) { #if TARGET_64BIT return (nuint)Exchange(ref Unsafe.As<nuint, long>(ref location1), (long)value); #else return (nuint)Exchange(ref Unsafe.As<nuint, int>(ref location1), (int)value); #endif } /// <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> /// <exception cref="NotSupportedException">An unsupported <typeparamref name="T"/> is specified.</exception> /// <typeparam name="T"> /// The type to be used for <paramref name="location1"/> and <paramref name="value"/>. /// This type must be a reference type, an enum type (i.e. typeof(T).IsEnum is true), or a primitive type (i.e. typeof(T).IsPrimitive is true). /// </typeparam> [Intrinsic] [return: NotNullIfNotNull(nameof(location1))] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe T Exchange<T>([NotNullIfNotNull(nameof(value))] ref T location1, T value) { // Handle all reference types with CompareExchange(ref object, ...). if (!typeof(T).IsValueType) { object? result = Exchange(ref Unsafe.As<T, object?>(ref location1), value); return Unsafe.As<object?, T>(ref result); } // Handle everything else with a CompareExchange overload for the unsigned integral type of the corresponding size. // Only primitive types and enum types (which are backed by primitive types) are supported. if (!typeof(T).IsPrimitive && !typeof(T).IsEnum) { throw new NotSupportedException(SR.NotSupported_ReferenceEnumOrPrimitiveTypeRequired); } if (sizeof(T) == 1) { return Unsafe.BitCast<byte, T>( Exchange( ref Unsafe.As<T, byte>(ref location1), Unsafe.BitCast<T, byte>(value))); } if (sizeof(T) == 2) { return Unsafe.BitCast<ushort, T>( Exchange( ref Unsafe.As<T, ushort>(ref location1), Unsafe.BitCast<T, ushort>(value))); } if (sizeof(T) == 4) { return Unsafe.BitCast<int, T>( Exchange( ref Unsafe.As<T, int>(ref location1), Unsafe.BitCast<T, int>(value))); } Debug.Assert(sizeof(T) == 8); return Unsafe.BitCast<long, T>( Exchange( ref Unsafe.As<T, long>(ref location1), Unsafe.BitCast<T, long>(value))); } #endregion #region CompareExchange /// <summary>Compares two 8-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.AggressiveInlining)] [CLSCompliant(false)] public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) => (sbyte)CompareExchange(ref Unsafe.As<sbyte, byte>(ref location1), (byte)value, (byte)comparand); /// <summary>Compares two 16-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> [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short CompareExchange(ref short location1, short value, short comparand) => (short)CompareExchange(ref Unsafe.As<short, ushort>(ref location1), (ushort)value, (ushort)comparand); /// <summary>Compares two 8-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> [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte CompareExchange(ref byte location1, byte value, byte comparand) { #if (MONO && (TARGET_ARM64 || TARGET_AMD64 || TARGET_WASM)) || (!MONO && (TARGET_X86 || TARGET_AMD64 || TARGET_ARM64)) return CompareExchange(ref location1, value, comparand); // Must expand intrinsic #else // this relies on GC keeping 4B alignment for refs and on subtracting to such alignment being in the same object nuint offset = Unsafe.OpportunisticMisalignment(ref location1, sizeof(uint)); ref uint alignedRef = ref Unsafe.As<byte, uint>(ref Unsafe.SubtractByteOffset(ref location1, offset)); int bitOffset = (int)((BitConverter.IsLittleEndian ? offset : sizeof(uint) - offset - sizeof(byte)) * 8); // to bit offset Debug.Assert(bitOffset is 0 or 8 or 16 or 24); uint mask = ~((uint)byte.MaxValue << bitOffset); uint shiftedValue = (uint)value << bitOffset; uint shiftedComparand = (uint)comparand << bitOffset; // this doesn't need to be volatile since CompareExchange will update stale values uint originalValue = alignedRef; uint fullComparand, newValue; do { // make sure the ref is still aligned Debug.Assert(Unsafe.IsOpportunisticallyAligned(ref alignedRef, sizeof(uint))); uint otherMemory = originalValue & mask; fullComparand = otherMemory | shiftedComparand; newValue = otherMemory | shiftedValue; } while (originalValue != (originalValue = CompareExchange(ref alignedRef, newValue, fullComparand))); // verify the GC hasn't broken the ref Debug.Assert((nuint)Unsafe.ByteOffset(ref Unsafe.As<uint, byte>(ref alignedRef), ref location1) == offset); return (byte)(originalValue >> bitOffset); #endif } /// <summary>Compares two 16-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.AggressiveInlining)] [CLSCompliant(false)] public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) { #if (MONO && (TARGET_ARM64 || TARGET_AMD64 || TARGET_WASM)) || (!MONO && (TARGET_X86 || TARGET_AMD64 || TARGET_ARM64)) return CompareExchange(ref location1, value, comparand); // Must expand intrinsic #else // this relies on GC keeping 4B alignment for refs and on subtracting to such alignment being in the same object nuint offset = Unsafe.OpportunisticMisalignment(ref location1, sizeof(uint)); ref uint alignedRef = ref Unsafe.As<ushort, uint>(ref Unsafe.SubtractByteOffset(ref location1, offset)); int bitOffset = (int)((BitConverter.IsLittleEndian ? offset : sizeof(uint) - offset - sizeof(ushort)) * 8); // to bit offset Debug.Assert(bitOffset is 0 or 16); uint mask = ~((uint)ushort.MaxValue << bitOffset); uint shiftedValue = (uint)value << bitOffset; uint shiftedComparand = (uint)comparand << bitOffset; // this doesn't need to be volatile since CompareExchange will update stale values uint originalValue = alignedRef; uint fullComparand, newValue; do { // make sure the ref is still aligned Debug.Assert(Unsafe.IsOpportunisticallyAligned(ref alignedRef, sizeof(uint))); uint otherMemory = originalValue & mask; fullComparand = otherMemory | shiftedComparand; newValue = otherMemory | shiftedValue; } while (originalValue != (originalValue = CompareExchange(ref alignedRef, newValue, fullComparand))); // verify the GC hasn't broken the ref Debug.Assert((nuint)Unsafe.ByteOffset(ref Unsafe.As<uint, ushort>(ref alignedRef), ref location1) == offset); return (ushort)(originalValue >> bitOffset); #endif } /// <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> [Intrinsic] [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> [Intrinsic] [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); /// <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.AggressiveInlining)] public static float CompareExchange(ref float location1, float value, float comparand) => Unsafe.BitCast<int, float>(CompareExchange(ref Unsafe.As<float, int>(ref location1), Unsafe.BitCast<float, int>(value), Unsafe.BitCast<float, int>(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.AggressiveInlining)] public static double CompareExchange(ref double location1, double value, double comparand) => Unsafe.BitCast<long, double>(CompareExchange(ref Unsafe.As<double, long>(ref location1), Unsafe.BitCast<double, long>(value), Unsafe.BitCast<double, long>(comparand))); /// <summary>Compares two native-sized signed integers for equality and, if they are equal, replaces the first one.</summary> /// <param name="location1">The destination, whose value is compared with the value of <paramref name="comparand"/> and possibly replaced by <paramref name="value"/>.</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.AggressiveInlining)] public static nint CompareExchange(ref nint location1, nint value, nint comparand) { #if TARGET_64BIT return (nint)CompareExchange(ref Unsafe.As<nint, long>(ref location1), (long)value, (long)comparand); #else return (nint)CompareExchange(ref Unsafe.As<nint, int>(ref location1), (int)value, (int)comparand); #endif } /// <summary>Compares two native-sized unsigned integers for equality and, if they are equal, replaces the first one.</summary> /// <param name="location1">The destination, whose value is compared with the value of <paramref name="comparand"/> and possibly replaced by <paramref name="value"/>.</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] [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static nuint CompareExchange(ref nuint location1, nuint value, nuint comparand) { #if TARGET_64BIT return (nuint)CompareExchange(ref Unsafe.As<nuint, long>(ref location1), (long)value, (long)comparand); #else return (nuint)CompareExchange(ref Unsafe.As<nuint, int>(ref location1), (int)value, (int)comparand); #endif } /// <summary>Compares two instances of the specified type <typeparamref name="T"/> for equality and, if they are equal, replaces the first one.</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 object 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> /// <exception cref="NotSupportedException">An unsupported <typeparamref name="T"/> is specified.</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, an enum type (i.e. typeof(T).IsEnum is true), or a primitive type (i.e. typeof(T).IsPrimitive is true). /// </typeparam> [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] [return: NotNullIfNotNull(nameof(location1))] public static unsafe T CompareExchange<T>(ref T location1, T value, T comparand) { // Handle all reference types with CompareExchange(ref object, ...). if (!typeof(T).IsValueType) { object? result = CompareExchange(ref Unsafe.As<T, object?>(ref location1), value, comparand); return Unsafe.As<object?, T>(ref result); } // Handle everything else with a CompareExchange overload for the unsigned integral type of the corresponding size. // Only primitive types and enum types (which are backed by primitive types) are supported. if (!typeof(T).IsPrimitive && !typeof(T).IsEnum) { throw new NotSupportedException(SR.NotSupported_ReferenceEnumOrPrimitiveTypeRequired); } if (sizeof(T) == 1) { return Unsafe.BitCast<byte, T>( CompareExchange( ref Unsafe.As<T, byte>(ref location1), Unsafe.BitCast<T, byte>(value), Unsafe.BitCast<T, byte>(comparand))); } if (sizeof(T) == 2) { return Unsafe.BitCast<ushort, T>( CompareExchange( ref Unsafe.As<T, ushort>(ref location1), Unsafe.BitCast<T, ushort>(value), Unsafe.BitCast<T, ushort>(comparand))); } if (sizeof(T) == 4) { return Unsafe.BitCast<int, T>( CompareExchange( ref Unsafe.As<T, int>(ref location1), Unsafe.BitCast<T, int>(value), Unsafe.BitCast<T, int>(comparand))); } Debug.Assert(sizeof(T) == 8); return Unsafe.BitCast<long, T>( CompareExchange( ref Unsafe.As<T, long>(ref location1), Unsafe.BitCast<T, long>(value), Unsafe.BitCast<T, 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 readonly ulong location) => CompareExchange(ref Unsafe.AsRef(in 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 original value in <paramref name="location1"/>.</returns> /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception> [Intrinsic] [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 oldValue; } 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 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 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 original value in <paramref name="location1"/>.</returns> /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception> [Intrinsic] [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 oldValue; } 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 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 And(ref ulong location1, ulong value) => (ulong)And(ref Unsafe.As<ulong, long>(ref location1), (long)value); /// <summary>Bitwise "ands" two values of type <typeparamref name="T"/> and replaces the first value 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 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> /// <exception cref="NotSupportedException">An unsupported <typeparamref name="T"/> is specified.</exception> /// <typeparam name="T"> /// The type to be used for <paramref name="location1"/> and <paramref name="value"/>. /// This type must be an integer primitive type or an enum type backed by an integer type. /// Floating-point types (float, double) are not supported. /// </typeparam> [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe T And<T>(ref T location1, T value) where T : struct { // Only integer primitive types and enum types backed by integer types are supported. // Floating-point types and floating-point backed enums are not supported. if ((!typeof(T).IsPrimitive && !typeof(T).IsEnum) || typeof(T) == typeof(float) || typeof(T) == typeof(double) || (typeof(T).IsEnum && (typeof(T).GetEnumUnderlyingType() == typeof(float) || typeof(T).GetEnumUnderlyingType() == typeof(double)))) { throw new NotSupportedException(SR.NotSupported_IntegerEnumOrPrimitiveTypeRequired); } // For 1-byte and 2-byte types, we need to use CompareExchange-based implementations // because there are no direct atomic And operations for these sizes. if (sizeof(T) == 1) { byte current = Unsafe.BitCast<T, byte>(location1); while (true) { byte newValue = (byte)(current & Unsafe.BitCast<T, byte>(value)); byte oldValue = CompareExchange( ref Unsafe.As<T, byte>(ref location1), newValue, current); if (oldValue == current) { return Unsafe.BitCast<byte, T>(oldValue); } current = oldValue; } } if (sizeof(T) == 2) { ushort current = Unsafe.BitCast<T, ushort>(location1); while (true) { ushort newValue = (ushort)(current & Unsafe.BitCast<T, ushort>(value)); ushort oldValue = CompareExchange( ref Unsafe.As<T, ushort>(ref location1), newValue, current); if (oldValue == current) { return Unsafe.BitCast<ushort, T>(oldValue); } current = oldValue; } } if (sizeof(T) == 4) { return Unsafe.BitCast<int, T>( And( ref Unsafe.As<T, int>(ref location1), Unsafe.BitCast<T, int>(value))); } Debug.Assert(sizeof(T) == 8); return Unsafe.BitCast<long, T>( And( ref Unsafe.As<T, long>(ref location1), Unsafe.BitCast<T, 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 original value in <paramref name="location1"/>.</returns> /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception> [Intrinsic] [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 oldValue; } 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 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 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 original value in <paramref name="location1"/>.</returns> /// <exception cref="NullReferenceException">The address of <paramref name="location1"/> is a null pointer.</exception> [Intrinsic] [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 oldValue; } 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 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 Or(ref ulong location1, ulong value) => (ulong)Or(ref Unsafe.As<ulong, long>(ref location1), (long)value); /// <summary>Bitwise "ors" two values of type <typeparamref name="T"/> and replaces the first value 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 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> /// <exception cref="NotSupportedException">An unsupported <typeparamref name="T"/> is specified.</exception> /// <typeparam name="T"> /// The type to be used for <paramref name="location1"/> and <paramref name="value"/>. /// This type must be an integer primitive type or an enum type backed by an integer type. /// Floating-point types (float, double) are not supported. /// </typeparam> [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe T Or<T>(ref T location1, T value) where T : struct { // Only integer primitive types and enum types backed by integer types are supported. // Floating-point types and floating-point backed enums are not supported. if ((!typeof(T).IsPrimitive && !typeof(T).IsEnum) || typeof(T) == typeof(float) || typeof(T) == typeof(double) || (typeof(T).IsEnum && (typeof(T).GetEnumUnderlyingType() == typeof(float) || typeof(T).GetEnumUnderlyingType() == typeof(double)))) { throw new NotSupportedException(SR.NotSupported_IntegerEnumOrPrimitiveTypeRequired); } // For 1-byte and 2-byte types, we need to use CompareExchange-based implementations // because there are no direct atomic Or operations for these sizes. if (sizeof(T) == 1) { byte current = Unsafe.BitCast<T, byte>(location1); while (true) { byte newValue = (byte)(current | Unsafe.BitCast<T, byte>(value)); byte oldValue = CompareExchange( ref Unsafe.As<T, byte>(ref location1), newValue, current); if (oldValue == current) { return Unsafe.BitCast<byte, T>(oldValue); } current = oldValue; } } if (sizeof(T) == 2) { ushort current = Unsafe.BitCast<T, ushort>(location1); while (true) { ushort newValue = (ushort)(current | Unsafe.BitCast<T, ushort>(value)); ushort oldValue = CompareExchange( ref Unsafe.As<T, ushort>(ref location1), newValue, current); if (oldValue == current) { return Unsafe.BitCast<ushort, T>(oldValue); } current = oldValue; } } if (sizeof(T) == 4) { return Unsafe.BitCast<int, T>( Or( ref Unsafe.As<T, int>(ref location1), Unsafe.BitCast<T, int>(value))); } Debug.Assert(sizeof(T) == 8); return Unsafe.BitCast<long, T>( Or( ref Unsafe.As<T, long>(ref location1), Unsafe.BitCast<T, long>(value))); } #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] public static void MemoryBarrier() => MemoryBarrier(); #endregion #if !MONO #region MemoryBarrierProcessWide [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Interlocked_MemoryBarrierProcessWide")] private static partial 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 #endif } }