Skip to content

Commit

Permalink
Add Interlocked.{Compare}Exchange for UIntPtr (#68966)
Browse files Browse the repository at this point in the history
  • Loading branch information
stephentoub authored May 7, 2022
1 parent 66b796a commit 118a162
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,22 @@ public static IntPtr Exchange(ref IntPtr location1, IntPtr value)
return (IntPtr)Interlocked.Exchange(ref Unsafe.As<IntPtr, int>(ref location1), (int)value);
#endif
}

/// <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>
[CLSCompliant(false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static UIntPtr Exchange(ref UIntPtr location1, UIntPtr value)
{
#if TARGET_64BIT
return (UIntPtr)Interlocked.Exchange(ref Unsafe.As<UIntPtr, long>(ref location1), (long)value);
#else
return (UIntPtr)Interlocked.Exchange(ref Unsafe.As<UIntPtr, int>(ref location1), (int)value);
#endif
}
#endregion

#region CompareExchange
Expand Down Expand Up @@ -123,6 +139,23 @@ public static IntPtr CompareExchange(ref IntPtr location1, IntPtr value, IntPtr
return (IntPtr)Interlocked.CompareExchange(ref Unsafe.As<IntPtr, int>(ref location1), (int)value, (int)comparand);
#endif
}

/// <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="UIntPtr"/>, 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="UIntPtr"/> that replaces the destination value if the comparison results in equality.</param>
/// <param name="comparand">The <see cref="UIntPtr"/> 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>
[CLSCompliant(false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static UIntPtr CompareExchange(ref UIntPtr location1, UIntPtr value, UIntPtr comparand)
{
#if TARGET_64BIT
return (UIntPtr)Interlocked.CompareExchange(ref Unsafe.As<UIntPtr, long>(ref location1), (long)value, (long)comparand);
#else
return (UIntPtr)Interlocked.CompareExchange(ref Unsafe.As<UIntPtr, int>(ref location1), (int)value, (int)comparand);
#endif
}
#endregion

#region Add
Expand Down
4 changes: 4 additions & 0 deletions src/libraries/System.Threading/ref/System.Threading.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ public static partial class Interlocked
public static int CompareExchange(ref int location1, int value, int comparand) { throw null; }
public static long CompareExchange(ref long location1, long value, long comparand) { throw null; }
public static System.IntPtr CompareExchange(ref System.IntPtr location1, System.IntPtr value, System.IntPtr comparand) { throw null; }
[System.CLSCompliantAttribute(false)]
public static System.UIntPtr CompareExchange(ref System.UIntPtr location1, System.UIntPtr value, System.UIntPtr comparand) { throw null; }
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("location1")]
public static object? CompareExchange(ref object? location1, object? value, object? comparand) { throw null; }
public static float CompareExchange(ref float location1, float value, float comparand) { throw null; }
Expand All @@ -200,6 +202,8 @@ public static partial class Interlocked
public static int Exchange(ref int location1, int value) { throw null; }
public static long Exchange(ref long location1, long value) { throw null; }
public static System.IntPtr Exchange(ref System.IntPtr location1, System.IntPtr value) { throw null; }
[System.CLSCompliantAttribute(false)]
public static System.UIntPtr Exchange(ref System.UIntPtr location1, System.UIntPtr 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; }
public static float Exchange(ref float location1, float value) { throw null; }
Expand Down
64 changes: 64 additions & 0 deletions src/libraries/System.Threading/tests/InterlockedTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,20 @@ public void InterlockedExchange_Int64()
Assert.Equal(12345, value);
}

[Fact]
public void InterlockedExchange_IntPtr()
{
nint value = 42;
Assert.Equal(42, (nint)Interlocked.Exchange(ref value, (nint)12345));
Assert.Equal(12345, value);

if (Environment.Is64BitProcess)
{
Assert.Equal(12345, (nint)Interlocked.Exchange(ref value, unchecked((nint)1 + int.MaxValue)));
Assert.Equal(unchecked((nint)1 + int.MaxValue), value);
}
}

[Fact]
public void InterlockedExchange_UInt64()
{
Expand All @@ -171,6 +185,20 @@ public void InterlockedExchange_UInt64()
Assert.Equal(12345u, value);
}

[Fact]
public void InterlockedExchange_UIntPtr()
{
nuint value = 42;
Assert.Equal(42u, (nuint)Interlocked.Exchange(ref value, (nuint)12345u));
Assert.Equal(12345u, value);

if (Environment.Is64BitProcess)
{
Assert.Equal(12345u, (nuint)Interlocked.Exchange(ref value, unchecked((nuint)1 + uint.MaxValue)));
Assert.Equal(unchecked((nuint)1 + uint.MaxValue), value);
}
}

[Fact]
public void InterlockedExchange_Float()
{
Expand Down Expand Up @@ -248,6 +276,24 @@ public void InterlockedCompareExchange_Int64()
Assert.Equal(12345, value);
}

[Fact]
public void InterlockedCompareExchange_IntPtr()
{
nint value = 42;

Assert.Equal(42, (nint)Interlocked.CompareExchange(ref value, (nint)12345, (nint)41));
Assert.Equal(42, value);

Assert.Equal(42, (nint)Interlocked.CompareExchange(ref value, (nint)12345, (nint)42));
Assert.Equal(12345, value);

if (Environment.Is64BitProcess)
{
Assert.Equal(12345, (nint)Interlocked.CompareExchange(ref value, unchecked((nint)1 + int.MaxValue), (nint)12345u));
Assert.Equal(unchecked((nint)1 + int.MaxValue), value);
}
}

[Fact]
public void InterlockedCompareExchange_UInt64()
{
Expand All @@ -260,6 +306,24 @@ public void InterlockedCompareExchange_UInt64()
Assert.Equal(12345u, value);
}

[Fact]
public void InterlockedCompareExchange_UIntPtr()
{
nuint value = 42;

Assert.Equal(42u, (nuint)Interlocked.CompareExchange(ref value, (nuint)12345u, (nuint)41u));
Assert.Equal(42u, value);

Assert.Equal(42u, (nuint)Interlocked.CompareExchange(ref value, (nuint)12345u, (nuint)42u));
Assert.Equal(12345u, value);

if (Environment.Is64BitProcess)
{
Assert.Equal(12345u, (nuint)Interlocked.CompareExchange(ref value, unchecked((nuint)1 + uint.MaxValue), (nuint)12345u));
Assert.Equal(unchecked((nuint)1 + uint.MaxValue), value);
}
}

[Fact]
public void InterlockedCompareExchange_Float()
{
Expand Down

0 comments on commit 118a162

Please sign in to comment.