Windows Critical Section - how to disable spinning

2019-07-24 20:36发布

问题:

I'm trying to set spin count for CRITICAL_SECTION to zero by different methods:

int main()
{
    CRITICAL_SECTION cs;

    ::InitializeCriticalSection(&cs);
    printf("Spin count by default %08X\n", cs.SpinCount);
    ::DeleteCriticalSection(&cs);

    ::InitializeCriticalSectionAndSpinCount(&cs, 0);
    printf("Spin count with zero spin count init %08X\n", cs.SpinCount );
    ::DeleteCriticalSection(&cs);


    ::InitializeCriticalSectionEx(&cs, 0, 0);
    printf("Spin count with zero spin count and flags init %08X\n", cs.SpinCount );
    ::DeleteCriticalSection(&cs);

    ::InitializeCriticalSection(&cs);
    ::SetCriticalSectionSpinCount(&cs, 0);
    printf("Spin count after explicit reset to zero %08X\n", cs.SpinCount);
    ::DeleteCriticalSection(&cs);
}

In Windows 7, all results are 0 as expected.

In Windows 10, except the last one, all result in 0x020007D0 value. The last one result in 0x02000000.

Apparently, 0x07D0 is actual spin count (2000 in decimal), and 0x02000000 is one of these flags:

#define RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO         0x01000000
#define RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN          0x02000000
#define RTL_CRITICAL_SECTION_FLAG_STATIC_INIT           0x04000000
#define RTL_CRITICAL_SECTION_FLAG_RESOURCE_TYPE         0x08000000
#define RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO      0x10000000

I'm afraid that RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN may cause critical section to spin even if I asked it not to spin by using SetCriticalSectionSpinCount.

Is there any way not to define RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN by using standard documented APIs ?

回答1:

After peeking into the implementation, figured out answer myself.

When InitializeCriticalSectionEx is used with nonzero spin count, RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN flag is not set. So this code outputs 00000001:

::InitializeCriticalSectionEx(&cs, 1, 0);
printf("Spin count after explicit one %08X\n", cs.SpinCount);
::DeleteCriticalSection(&cs);

One spin count is almost zero spin count. And, moreover, with calling SetCriticalSectionSpinCount afterwards it can be reset to zero. So this code outputs 00000000:

::InitializeCriticalSectionEx(&cs, 1, 0);
::SetCriticalSectionSpinCount(&cs, 0);
printf("Spin count after explicit one and then reset to zero %08X\n", cs.SpinCount);
::DeleteCriticalSection(&cs);

Sure, there should be compelling reason to disable spinning. By default, as @JonathanPotter pointed out, spinning is good. Otherwise it wouldn't have been set as default behavior. So I didn't even apply the solution for disabling spinning to my original problem.

On the other hand, it may be not the intention of maintainers of critical section to disrespect zero spin count passed to InitializeCriticalSectionEx or InitializeCriticalSectionAndSpinCount. They just made sure that plain InitializeCriticalSection gets automatic spin count.