// Busy spin for the given number of iterations.
EXTERN_C NATIVEAOT_API void __cdecl RhSpinWait(int32_t iterations)
{
+ ASSERT(iterations > 0);
+
+ // limit the spin count in coop mode.
+ ASSERT_MSG(iterations <= 10000 || !ThreadStore::GetCurrentThread()->IsCurrentThreadInCooperativeMode(),
+ "This is too long wait for coop mode. You must p/invoke with GC transition.");
+
YieldProcessorNormalizationInfo normalizationInfo;
YieldProcessorNormalizedForPreSkylakeCount(normalizationInfo, iterations);
}
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
internal static partial void RhSpinWait(int iterations);
+ // Call RhSpinWait with a GC transition
+ [LibraryImport(RuntimeLibrary, EntryPoint = "RhSpinWait")]
+ [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
+ internal static partial void RhLongSpinWait(int iterations);
+
// Yield the cpu to another thread ready to process, if one is available.
[LibraryImport(RuntimeLibrary, EntryPoint = "RhYield")]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
/// </summary>
internal const int OptimalMaxSpinWaitsPerSpinIteration = 64;
- public static void SpinWait(int iterations) => RuntimeImports.RhSpinWait(iterations);
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void LongSpinWait(int iterations)
+ {
+ RuntimeImports.RhLongSpinWait(iterations);
+ }
+
+ public static void SpinWait(int iterations)
+ {
+ if (iterations <= 0)
+ return;
+
+ // Max iterations to be done in RhSpinWait.
+ // RhSpinWait does not switch GC modes and we want to avoid native spinning in coop mode for too long.
+ const int spinWaitCoopThreshold = 10000;
+
+ if (iterations > spinWaitCoopThreshold)
+ {
+ LongSpinWait(iterations);
+ }
+ else
+ {
+ RuntimeImports.RhSpinWait(iterations);
+ }
+ }
[MethodImpl(MethodImplOptions.NoInlining)] // Slow path method. Make sure that the caller frame does not pay for PInvoke overhead.
public static bool Yield() => RuntimeImports.RhYield();