Hi.
I have a C# project, where I have to both access the current workload of my processor, and ensure, that I run some specific code on every kernel of the processor. My problem is, that accessing the workload of my processor seems to prevent me from correctly assigning a thread affinity mask. I have some code here, that illustrates the problem:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace KernelAffinitySpike
{
class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
private static extern UIntPtr SetThreadAffinityMask(IntPtr hThread, UIntPtr dwThreadAffinityMask);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetCurrentThread();
private static PerformanceCounter cpuUsage;
private static UIntPtr oldMask, newMask, testMask; // thread-level processor affinity masks.
static void Main(string[] args)
{
InitPerformanceCounter();
Console.WriteLine("Pre: thread affinity: " + CurrentThreadAffinityMask());
if (AllKernelsAccessible())
Console.WriteLine("Pre: all kernels accessible");
else
{
Console.Write("Pre: some kernels not accessible: ");
foreach (UInt32 kernel in InaccessibleKernels())
Console.Write(kernel + " ");
Console.WriteLine();
}
float load = cpuUsage.NextValue();
Console.WriteLine("Post: thread affinity: " + CurrentThreadAffinityMask());
if (AllKernelsAccessible())
Console.WriteLine("Post: all kernels accessible");
else
{
Console.Write("Post: some kernels not accessible: ");
foreach (UInt32 kernel in InaccessibleKernels())
Console.Write(kernel + " ");
Console.WriteLine();
}
Console.ReadLine();
}
static void InitPerformanceCounter()
{
cpuUsage = new PerformanceCounter();
cpuUsage.CategoryName = "Processor";
cpuUsage.CounterName = "% Processor Time";
cpuUsage.InstanceName = "_Total";
}
static UInt32 CurrentThreadAffinityMask()
{
oldMask = SetThreadAffinityMask(GetCurrentThread(), (UIntPtr) 3); // 3 just enables all processors on a dual core. I'm only interested in the return value.
SetThreadAffinityMask(GetCurrentThread(), oldMask);
return (UInt32) oldMask;
}
static List<UInt32> InaccessibleKernels()
{
List<UInt32> inaccessible = new List<UInt32>();
for (int i = 0; i < Environment.ProcessorCount; i++)
{
newMask = (UIntPtr)(1 << i);
oldMask = SetThreadAffinityMask(GetCurrentThread(), newMask);
testMask = SetThreadAffinityMask(GetCurrentThread(), oldMask);
if (newMask != testMask)
inaccessible.Add((UInt32) newMask);
}
return inaccessible;
}
static bool AllKernelsAccessible()
{
return InaccessibleKernels().Count == 0;
}
}
}
Running this code yields the following output:
Pre: thread affinity: 3
Pre: all kernels accessible
Post: thread affinity: 2
Post: some kernels not accessible: 1
So, it seems that the cpuUsage.NextValue call somehow changes the thread affinity mask, and also makes it impossible to change the mask to 1. It does make sense, that the Nextvalue call would have to interact with the thread affinity mask in some way, if it is aggregating a performance count from each kernel, but I cannot understand, why it should affect future changes to the thread affinity mask. Does anybody have an explanation, or a workaround to this problem?