Skip to content

Commit

Permalink
[release/9.0] [NativeAOT] Introduce pointer-based CompareExchange int…
Browse files Browse the repository at this point in the history
…rinsic and use operating with syncblock bits. (#106727)

* introduce CompareExchange(int* location1, int value, int comparand)

* Apply suggestions from code review

Co-authored-by: Jan Kotas <[email protected]>

* fix arm32 build

---------

Co-authored-by: vsadov <[email protected]>
Co-authored-by: Vladimir Sadov <[email protected]>
Co-authored-by: Jan Kotas <[email protected]>
Co-authored-by: Jeff Schwartz <[email protected]>
  • Loading branch information
5 people authored Aug 21, 2024
1 parent a7154bd commit 02bb8c3
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,10 @@ internal static IntPtr RhGetModuleSection(TypeManagerHandle module, ReadyToRunSe
[RuntimeImport(RuntimeLibrary, "RhpLockCmpXchg32")]
internal static extern int InterlockedCompareExchange(ref int location1, int value, int comparand);

[MethodImplAttribute(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhpLockCmpXchg32")]
internal static extern unsafe int InterlockedCompareExchange(int* location1, int value, int comparand);

[MethodImplAttribute(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhpLockCmpXchg64")]
internal static extern long InterlockedCompareExchange(ref long location1, long value, long comparand);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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;
using System.Runtime.CompilerServices;
Expand All @@ -23,6 +24,24 @@ public static int CompareExchange(ref int location1, int value, int comparand)
#endif
}

// This is used internally by NativeAOT runtime in cases where having a managed
// ref to the location is unsafe (Ex: it is the syncblock of a pinned object).
// The intrinsic expansion for this overload is exactly the same as for the `ref int`
// variant and will go on the same path since expansion is triggered by the name and
// return type of the method.
// The important part is avoiding `ref *location` in the unexpanded scenario, like
// in a case when compiling the Debug flavor of the app.
[Intrinsic]
internal static unsafe int CompareExchange(int* location1, int value, int comparand)
{
#if TARGET_X86 || TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64
return CompareExchange(location1, value, comparand); // Must expand intrinsic
#else
Debug.Assert(location1 != null);
return RuntimeImports.InterlockedCompareExchange(location1, value, comparand);
#endif
}

[Intrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long CompareExchange(ref long location1, long value, long comparand)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ private static unsafe int AssignHashCode(object o, int* pHeader)
// there is nothing - try set hashcode inline
Debug.Assert((oldBits & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) == 0);
int newBits = BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX | BIT_SBLK_IS_HASHCODE | oldBits | newHash;
if (Interlocked.CompareExchange(ref *pHeader, newBits, oldBits) == oldBits)
if (Interlocked.CompareExchange(pHeader, newBits, oldBits) == oldBits)
{
return newHash;
}
Expand Down Expand Up @@ -247,7 +247,7 @@ public static unsafe void SetSyncEntryIndex(int* pHeader, int syncIndex)
newBits = oldBits & ~(BIT_SBLK_IS_HASHCODE | MASK_HASHCODE_INDEX);
newBits |= syncIndex | BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX;
}
while (Interlocked.CompareExchange(ref *pHeader, newBits, oldBits) != oldBits);
while (Interlocked.CompareExchange(pHeader, newBits, oldBits) != oldBits);
}

//
Expand Down Expand Up @@ -312,7 +312,7 @@ public static unsafe int TryAcquire(object obj, int currentThreadID, bool oneSho
// N.B. hashcode, thread ID and sync index are never 0, and hashcode is largest of all
if ((oldBits & MASK_HASHCODE_INDEX) == 0)
{
if (Interlocked.CompareExchange(ref *pHeader, oldBits | currentThreadID, oldBits) == oldBits)
if (Interlocked.CompareExchange(pHeader, oldBits | currentThreadID, oldBits) == oldBits)
{
return -1;
}
Expand Down Expand Up @@ -369,7 +369,7 @@ private static unsafe int TryAcquireUncommon(object obj, int currentThreadID, bo
if ((oldBits & MASK_HASHCODE_INDEX) == 0)
{
int newBits = oldBits | currentThreadID;
if (Interlocked.CompareExchange(ref *pHeader, newBits, oldBits) == oldBits)
if (Interlocked.CompareExchange(pHeader, newBits, oldBits) == oldBits)
{
return -1;
}
Expand Down Expand Up @@ -398,7 +398,7 @@ private static unsafe int TryAcquireUncommon(object obj, int currentThreadID, bo
int newBits = oldBits + SBLK_LOCK_RECLEVEL_INC;
if ((newBits & SBLK_MASK_LOCK_RECLEVEL) != 0)
{
if (Interlocked.CompareExchange(ref *pHeader, newBits, oldBits) == oldBits)
if (Interlocked.CompareExchange(pHeader, newBits, oldBits) == oldBits)
{
return -1;
}
Expand Down Expand Up @@ -458,7 +458,7 @@ public static unsafe void Release(object obj)
oldBits - SBLK_LOCK_RECLEVEL_INC :
oldBits & ~SBLK_MASK_LOCK_THREADID;

if (Interlocked.CompareExchange(ref *pHeader, newBits, oldBits) == oldBits)
if (Interlocked.CompareExchange(pHeader, newBits, oldBits) == oldBits)
{
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ internal static IntPtr RhGetModuleSection(TypeManagerHandle module, ReadyToRunSe
[RuntimeImport(RuntimeLibrary, "RhpLockCmpXchg32")]
internal static extern int InterlockedCompareExchange(ref int location1, int value, int comparand);

[MethodImplAttribute(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhpLockCmpXchg32")]
internal static extern unsafe int InterlockedCompareExchange(int* location1, int value, int comparand);

[MethodImplAttribute(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhpLockCmpXchg64")]
internal static extern long InterlockedCompareExchange(ref long location1, long value, long comparand);
Expand Down

0 comments on commit 02bb8c3

Please sign in to comment.