-FCIMPL0(UINT32, SystemNative::GetCurrentProcessorNumber)
-{
- FCALL_CONTRACT;
-
- return ::GetCurrentProcessorNumber();
-}
-FCIMPLEND;
-
-
FCIMPL0(UINT32, SystemNative::GetTickCount)
{
public:
// Functions on the System.Environment class
static FCDECL0(INT64, __GetSystemTimeAsFileTime);
- static FCDECL0(UINT32, GetCurrentProcessorNumber);
static FCDECL0(UINT32, GetTickCount);
static
using System.Runtime.CompilerServices;
using System.Threading;
+using Internal.Runtime.Augments;
+
namespace System.Buffers
{
/// <summary>
// Try to push on to the associated stack first. If that fails,
// round-robin through the other stacks.
LockedStack[] stacks = _perCoreStacks;
- int index = Environment.CurrentExecutionId % stacks.Length;
+ int index = RuntimeThread.GetCurrentProcessorId() % stacks.Length;
for (int i = 0; i < stacks.Length; i++)
{
if (stacks[index].TryPush(array)) return;
// round-robin through the other stacks.
T[] arr;
LockedStack[] stacks = _perCoreStacks;
- int index = Environment.CurrentExecutionId % stacks.Length;
+ int index = RuntimeThread.GetCurrentProcessorId() % stacks.Length;
for (int i = 0; i < stacks.Length; i++)
{
if ((arr = stacks[index].TryPop()) != null) return arr;
}
}
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern int GetCurrentProcessorNumber();
+
+ // The upper bits of t_currentProcessorIdCache are the currentProcessorId. The lower bits of
+ // the t_currentProcessorIdCache are counting down to get it periodically refreshed.
+ // TODO: Consider flushing the currentProcessorIdCache on Wait operations or similar
+ // actions that are likely to result in changing the executing core
+ [ThreadStatic]
+ private static int t_currentProcessorIdCache;
+
+ private const int ProcessorIdCacheShift = 16;
+ private const int ProcessorIdCacheCountDownMask = (1 << ProcessorIdCacheShift) - 1;
+ private const int ProcessorIdRefreshRate = 5000;
+
+ private static int RefreshCurrentProcessorId()
+ {
+ int currentProcessorId = GetCurrentProcessorNumber();
+
+ // On Unix, GetCurrentProcessorNumber() is implemented in terms of sched_getcpu, which
+ // doesn't exist on all platforms. On those it doesn't exist on, GetCurrentProcessorNumber()
+ // returns -1. As a fallback in that case and to spread the threads across the buckets
+ // by default, we use the current managed thread ID as a proxy.
+ if (currentProcessorId < 0) currentProcessorId = Environment.CurrentManagedThreadId;
+
+ // Add offset to make it clear that it is not guaranteed to be 0-based processor number
+ currentProcessorId += 100;
+
+ Debug.Assert(ProcessorIdRefreshRate <= ProcessorIdCacheCountDownMask);
+
+ // Mask with Int32.MaxValue to ensure the execution Id is not negative
+ t_currentProcessorIdCache = ((currentProcessorId << ProcessorIdCacheShift) & Int32.MaxValue) | ProcessorIdRefreshRate;
+
+ return currentProcessorId;
+ }
+
+ // Cached processor id used as a hint for which per-core stack to access. It is periodically
+ // refreshed to trail the actual thread core affinity.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int GetCurrentProcessorId()
+ {
+ int currentProcessorIdCache = t_currentProcessorIdCache--;
+ if ((currentProcessorIdCache & ProcessorIdCacheCountDownMask) == 0)
+ return RefreshCurrentProcessorId();
+ return (currentProcessorIdCache >> ProcessorIdCacheShift);
+ }
+
public static void SpinWait(int iterations) => Thread.SpinWait(iterations);
public static bool Yield() => Thread.Yield();
}
}
- internal static extern int CurrentProcessorNumber
- {
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- get;
- }
-
- // The upper bits of t_executionIdCache are the executionId. The lower bits of
- // the t_executionIdCache are counting down to get it periodically refreshed.
- // TODO: Consider flushing the executionIdCache on Wait operations or similar
- // actions that are likely to result in changing the executing core
- [ThreadStatic]
- private static int t_executionIdCache;
-
- private const int ExecutionIdCacheShift = 16;
- private const int ExecutionIdCacheCountDownMask = (1 << ExecutionIdCacheShift) - 1;
- private const int ExecutionIdRefreshRate = 5000;
-
- private static int RefreshExecutionId()
- {
- int executionId = CurrentProcessorNumber;
-
- // On Unix, CurrentProcessorNumber is implemented in terms of sched_getcpu, which
- // doesn't exist on all platforms. On those it doesn't exist on, GetCurrentProcessorNumber
- // returns -1. As a fallback in that case and to spread the threads across the buckets
- // by default, we use the current managed thread ID as a proxy.
- if (executionId < 0) executionId = Environment.CurrentManagedThreadId;
-
- Debug.Assert(ExecutionIdRefreshRate <= ExecutionIdCacheCountDownMask);
-
- // Mask with Int32.MaxValue to ensure the execution Id is not negative
- t_executionIdCache = ((executionId << ExecutionIdCacheShift) & Int32.MaxValue) | ExecutionIdRefreshRate;
-
- return executionId;
- }
-
- // Cached processor number used as a hint for which per-core stack to access. It is periodically
- // refreshed to trail the actual thread core affinity.
- internal static int CurrentExecutionId
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- int executionIdCache = t_executionIdCache--;
- if ((executionIdCache & ExecutionIdCacheCountDownMask) == 0)
- return RefreshExecutionId();
- return (executionIdCache >> ExecutionIdCacheShift);
- }
- }
-
public static string GetEnvironmentVariable(string variable)
{
if (variable == null)
// 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;
+using System.Security;
+using Microsoft.Win32;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.ConstrainedExecution;
+using System.Runtime.Versioning;
+using System.Diagnostics;
+using System.Diagnostics.Tracing;
+using Microsoft.Win32.SafeHandles;
+
+using Internal.Runtime.Augments;
namespace System.Threading
{
- using System;
- using System.Security;
- using Microsoft.Win32;
- using System.Runtime.CompilerServices;
- using System.Runtime.InteropServices;
- using System.Runtime.ConstrainedExecution;
- using System.Runtime.Versioning;
- using System.Diagnostics;
- using System.Diagnostics.Tracing;
- using Microsoft.Win32.SafeHandles;
-
-
-
public delegate void TimerCallback(Object state);
//
m_dueTime = Timeout.UnsignedInfinite;
m_period = Timeout.UnsignedInfinite;
m_executionContext = ExecutionContext.Capture();
- m_associatedTimerQueue = TimerQueue.Instances[Environment.CurrentExecutionId % TimerQueue.Instances.Length];
+ m_associatedTimerQueue = TimerQueue.Instances[RuntimeThread.GetCurrentProcessorId() % TimerQueue.Instances.Length];
//
// After the following statement, the timer may fire. No more manipulation of timer state outside of
FCIMPLEND
+FCIMPL0(INT32, ThreadNative::GetCurrentProcessorNumber)
+{
+ FCALL_CONTRACT;
+
+ return ::GetCurrentProcessorNumber();
+}
+FCIMPLEND;
static FCDECL2(void, SetAbortReason, ThreadBaseObject* pThisUNSAFE, Object* pObject);
static FCDECL1(void, ClearAbortReason, ThreadBaseObject* pThisUNSAFE);
+ static FCDECL0(INT32, GetCurrentProcessorNumber);
+
private:
struct KickOffThread_Args {
FCFuncElement("get_HasShutdownStarted", SystemNative::HasShutdownStarted)
QCFuncElement("GetProcessorCount", SystemNative::GetProcessorCount)
FCFuncElement("GetCommandLineArgsNative", SystemNative::GetCommandLineArgs)
- FCFuncElement("get_CurrentProcessorNumber", SystemNative::GetCurrentProcessorNumber)
#if defined(FEATURE_COMINTEROP)
QCFuncElement("WinRTSupported", SystemNative::WinRTSupported)
FCFuncElement("InterruptInternal", ThreadNative::Interrupt)
FCFuncElement("JoinInternal", ThreadNative::Join)
QCFuncElement("GetOptimalMaxSpinWaitsPerSpinIterationInternal", ThreadNative::GetOptimalMaxSpinWaitsPerSpinIteration)
+ FCFuncElement("GetCurrentProcessorNumber", ThreadNative::GetCurrentProcessorNumber)
FCFuncEnd()
FCFuncStart(gThreadFuncs)