none
CriticalSection Changed.

    Question

  • I was reading the article at http://msdn.microsoft.com/windowsvista/default.aspx?pull=/library/en-us/dnlong/html/AppComp.asp and noticed the section about critical sections.  There is a bullet point that says the following.

    "Should prevent starvation. Applications that call Sleep while holding the critical section lock now can cause starvation for other threads that need the lock. Sleep calls should be placed after the LeaveCriticalSection call. "

    I would really like some more information on this. Is that saying that if i sleep while i own a critical section that another thread trying to obtain the critical section may have problems (other than it blocking until the owning thread releases the critical section)?  If so why and under what specific conditions would this occur?.

     

    Tuesday, June 27, 2006 7:48 PM

Answers

  • Critical sections are no longer acquired in FIFO order, this since Windows2003 SP1. This means that on W2K3 and higher, in your sample, the same thread will stay running after leaving the critical section , while on XP or lower, the thread will pre-empt and the next waiting thread in FIFO order will get scheduled. This is done to prevent performnce degradation due to excessive context switching.

    Willy.

     

     

     

    Monday, December 11, 2006 10:48 AM

All replies

  • Ok,  I did some testing and figured out the answer to this one.

    Lets take the following example. 

    1. Thread A obtains the crit section.

    2.  Thread B attempts to obtain the crit sect and blocks waiting for the crit sect to be free.

    3. Thread A calls sleep(100);

    4. Thread A. releases the critical section.

    5. Thread A calls EnterCriticalSection again  immedietaly.

    ... this is there they differ in behavior.

    6A. In windows xp,  thread B will be given priority and obtain the lock and thread A will block waiting for the critical section to be free. 

    6B. In windows Vista thread B will continue to wait and thread A will succeed in obtaining the lock again.

    Here is a simple test you can do to see this.

    Code

    DWORD WINAPI ThreadA(LPVOID lpVoid);
    DWORD WINAPI ThreadB(LPVOID lpVoid);
    void Start();

    CRITICAL_SECTION g_critSect;
    void Start()
    {
        ::InitializeCriticalSection(&g_critSect);
        DWORD dwIgnore;

        //create both threads
        OutputDebugString("Start() is called\n");
        ::CreateThread(NULL,0,ThreadA,0,NULL,&dwIgnore);
        ::CreateThread(NULL,0,ThreadB,0,NULL,&dwIgnore);

    }

    DWORD WINAPI ThreadA(LPVOID lpVoid)
    {
        for(int x = 0;  x < 2; x++)
        {
            for(int i = 1; i < 3; i++)
            {

                ::EnterCriticalSection(&g_critSect);
                OutputDebugString(_T("thread A\n"));
                ::Sleep(250*i);
                ::LeaveCriticalSection(&g_critSect);
            }
        }

        return 0;
    }


    DWORD WINAPI ThreadB(LPVOID lpVoid)
    {
        //just a sloppy way to make sure thread A obtains the lock first.
        ::Sleep(100);
        for(int x = 0;  x < 2; x++)
        {
            for(int i = 1; i < 3; i++)
            {

                ::EnterCriticalSection(&g_critSect);
                OutputDebugString(_T("thread B\n"));
                ::Sleep(250*i);
                ::LeaveCriticalSection(&g_critSect);
            }
        }
        return 0;
    }

    Output - Windows Xp

    Start() is called
    thread A
    thread B
    thread A
    thread B
    thread A
    thread B
    thread A
    thread B
    The thread 'Win32 Thread' (0x80c) has exited with code 0 (0x0).
    The thread 'Win32 Thread' (0x7a8) has exited with code 0 (0x0).

    Output - Windows Vista

    Start() is called
    thread A
    thread A
    thread A
    thread A
    The thread 'Win32 Thread' (0xfb4) has exited with code 0 (0x0).
    thread B
    thread B
    thread B
    thread B
    The thread 'Win32 Thread' (0xe5c) has exited with code 0 (0x0).

     

    Wednesday, June 28, 2006 2:11 PM
  •  JonnyDeep wrote:

    I was reading the article at http://msdn.microsoft.com/windowsvista/default.aspx?pull=/library/en-us/dnlong/html/AppComp.asp and noticed the section about critical sections.  There is a bullet point that says the following.

    "Should prevent starvation. Applications that call Sleep while holding the critical section lock now can cause starvation for other threads that need the lock. Sleep calls should be placed after the LeaveCriticalSection call. "

    I would really like some more information on this. Is that saying that if i sleep while i own a critical section that another thread trying to obtain the critical section may have problems (other than it blocking until the owning thread releases the critical section)?  If so why and under what specific conditions would this occur?.

    Calling Sleep before calling LeaveCriticalSection means the thread is relinquishing control of itself without unlocking the the critical section.  It's logical that if the thread is not running (sleeping) then it should not need a lock (to a critical section in this case) than that the critical section is locked without any code running and using the lock.

    Tuesday, August 15, 2006 3:37 PM
  • >> 3. Thread A calls sleep(100);

    Identical behavior is seen even if thread A performs another non-instant activity.
    For example:

    DWORD WINAPI ThreadA(LPVOID lpVoid)
    {
    ...
                //::Sleep(250*i);
                FILE *f = fopen ("tmp", "a");
                if (f != NULL)
                {
                           
    for(int j = 1; j < i*1000000; j++)
                                        fprintf (f, "%d ", j);
                            fclose (f);
                }
    ...
    }


    How can this behavior be reasoned?
    Why is ThreadA constantly preferred over ThreadB?

    Wednesday, December 06, 2006 11:48 AM
  • Critical sections are no longer acquired in FIFO order, this since Windows2003 SP1. This means that on W2K3 and higher, in your sample, the same thread will stay running after leaving the critical section , while on XP or lower, the thread will pre-empt and the next waiting thread in FIFO order will get scheduled. This is done to prevent performnce degradation due to excessive context switching.

    Willy.

     

     

     

    Monday, December 11, 2006 10:48 AM
  • After reading this I just want to ask why miscrosoft has called it starvation. Finally, if Vista does not re-schedule the threads after each LeaveCriticalSection it may turn out that the performance will be even higher.

    BTW, did anybody check that sample with changed threadA priority? It would be good to know if a higher priority provides it with a chance to get timeslice immediately after the 1st threadB's LeaveCriticalSection() or re-scheduling is not done even if boosted threads are waiting and that's what MS meant under starvation

    Wednesday, May 02, 2007 11:19 PM
  •  JVlad wrote:

    After reading this I just want to ask why miscrosoft has called it starvation. Finally, if Vista does not re-schedule the threads after each LeaveCriticalSection it may turn out that the performance will be even higher.

    BTW, did anybody check that sample with changed threadA priority? It would be good to know if a higher priority provides it with a chance to get timeslice immediately after the 1st threadB's LeaveCriticalSection() or re-scheduling is not done even if boosted threads are waiting and that's what MS meant under starvation

    It's called starvation because no other threads can get that synchronization object.  Calling Thread.Sleep explicitly tells the system to sleep the thread for a minimum amount of time, priority has no bearing.  The OS can't reschedule a thread that you've explicity slept--it would be a contradiction to what the application has requested.
    Thursday, May 03, 2007 3:30 PM
  • I hope, it's not a Vista related thing that no other threads can't get that synchronization object. It's just a behaviour by design. When critical section as an object was created it was created  for this purpose to prevent other threads from that...

     

    Regarding starvation it still confuses me. Vista comes with precaution that if a thread owning a critical section calls Sleep(), starvation may happen  for the threads waiting for the that critical section.  Am I wrong with understanding this? What if it does not call Sleep but still executes a long loop of multi-million operations? Would this cause starvation? Why there are no such precautions for the pre-Vista operating systems?

     

    From the original post (2nd one actually) it looks like pre-Vista windows does re-schedule threads when one of them leaves critical section while Vista does not do this. Could you confirm this?

    If so, will Vista re-schedule if there are higher-priority threads are waiting for the lock? If it does not, it looks like a potential reason for performnce degradation.

    Friday, May 04, 2007 12:39 AM