views:

464

answers:

2

I run my NT service on an Intel Core2 based Win2k3 machine where I need to iterate through all logical CPUs (all bits in process affinity). To do so I call GetProcessAffinityMask() to retrieve the system affinity mask and then switch the process to each processor in turn:

 DWORD systemMask;
 GetProcessAffinityMask( ... &systemMask );
 DWORD processorId = 1;
 while( systemMask != 0 ) {
    SetProcessAffinityMask(... processorId );
    Sleep( 1 ); // to be sure that it shifts to that processor
    systemMask >>= 1;
    processorId <<= 1;
 }

On each iteration I invoke code from here to retrieve the current processor APIC id. The problem is that for different processors it sometimes returns identical APIC ids. According to documentation each processor in the system must have an identical id.

I tried debugging this - checked whether Windows actually changes the affinity:

 while( systemMask != 0 ) {
    SetProcessAffinityMask(... processorId );
    Sleep( 1 ); // to be sure that it shifts to that processor
    DWORD tempAffinity;
    GetProcessAffinityMask( ... &tempAffinity );
    // run APIC id detection code here
    systemMask >>= 1;
    processorId <<= 1;
 }

It returns exactly the affinity mask I expect but APIC ids can still be the same for different processors.

Is there an explanation to this weird situation?

+1  A: 

It's because cpuid can't be used in MSVC++ inline __asm statements because it trashes the registers (i.e. the compiler stored some variable in eax, you called cpuid which changed the register behind the compiler's back). MSVC++ compiler has no equivalent to GCC's clobber list, so it won't work.

You need to use an alternate method to identify the currently running CPU, though off the top of my head, I can't think of a good one...

Edit: Also, why do you care about the APIC ID? If all you want to do is execute the same code n times on n processors in a sequential fashion, can't you just set the affinity, Sleep, increment affinity, Sleep, etc.?

Paul Betts
If that was the case wouldn't this be reproduceable on all machines? I compile once and run on multiple machines and only a couple of them exhibit this behaviour, all others work as expected.
sharptooth
Hmmm, yes it would... Your best bet is to bust out WinDbg and investigate further. I also do remember a legitimate Intel errata about this that we had to work around in the OS, so it could be possible that you found it if it's 100% repro on specific machines. Are these relatively new CPUs?
Paul Betts
A: 

In the first iteration of the loop, it appears that you set the affinity mask to 0. The MSDN docs don't clearly specify what the behavior should be in that case, but I would bet it would let the thread run anywhere in that case.

boiler96
Actually, isn't processorID always 0 in this code? It starts 0, and then processorID <<= 1 should result in 0, right?
boiler96
Very true - I incorrectly cited the code. It actually starts with 1.
sharptooth
Then I suspect your APIC ID detection code. Can we see that?
boiler96