I have a problem using critical sections. My application has a large number of threads, say 60, which all need access to a global resource. Therefore, I protect this resource with a critical sector. This works great while running, however, when my application shuts down, I start the threads to exit and then destroy the critical section.
A problem arises if some of these threads are waiting in the critical section during exit and, thus, are blocked upon exit from it.
I wrote a wrapper around the Windows CriticalSection calls with the “Initialised” flag, which I set to true when creating the crit and set it to false when I was going to leave crit (both cases: set when inside the crit). This flag is checked before the "enter crit" function tries to enter crit, bypassing the request if the flag is false. The flag is also checked at the moment when any thread successfully enters crit, so it immediately leaves crit if it is erroneous.
What should I do before removing the crit, set the flag to false, and then wait until all pending threads are: allowed in crit; see flag Initialized is false; then leave crit (which should be pretty fast for each thread).
I check the number of threads waiting to access the critical criterion, checking the LockCount inside the CRITICAL_SECTION structure and waiting until it reaches 0 (in XP,; LockCount - (RecursionCount-1)in 2003 the server and higher the number of locks ((-1) - (LockCount)) >> 2), before I then destroy the critical section.
This should be enough, however, I found that LockCount reaches 0 when there is another thread (always only one thread, never again) waiting to enter crit, that is, if I remove crit at this moment, another thread then wakes up from waiting on crit and causes a crash, since the CRITICAL_SECTION object had been destroyed by then.
, , ; , , , , , .
- , LockCount CRITICAL_SECTION 1? , CRITICAL_SECTION , ( , ), 0...
, , ?
struct:
typedef struct MY_CRIT {
BOOL Initialised;
CRITICAL_SECTION Crit;
int MyLockCount;
}
init init:
BOOL InitCrit( MY_CRIT *pCrit )
{
if (pCrit)
{
InitializeCriticalSection( &pCrit->Crit );
pCrit->Initialised = TRUE;
pCrit->MyLockCount = 0;
return TRUE;
}
else
return FALSE;
}
:
BOOL EnterCrit( MY_CRIT *pCrit )
{
if (pCrit && pCrit->Initialised)
{
pCrit->MyLockCount++;
EnterCriticalSection( &pCrit->Crit );
pCrit->MyLockCount--;
if (pCrit->Initialised)
{
return TRUE;
}
else
{
LeaveCriticalSection( &pCrit->Crit );
return FALSE;
}
}
else
return FALSE;
}
FreeCrit:
void FreeCrit( MY_CRIT *pCrit )
{
LONG WaitingCount = 0;
if (pCrit && (pCrit->Initialised))
{
EnterCriticalSection( &pCrit->Crit );
pCrit->Initialised = FALSE;
LeaveCriticalSection( &pCrit->Crit );
do {
EnterCriticalSection( &pCrit->Crit );
if (IsWindowsXPOrBelow())
{
if ((pCrit->Crit.LockCount > 0) && ((pCrit->Crit.RecursionCount - 1) >= 0))
WaitingCount = pCrit->Crit.LockCount - (pCrit->Crit.RecursionCount - 1);
else
WaitingCount = 0;
}
else
{
WaitingCount = ((-1) - (pCrit->Crit.LockCount)) >> 2;
}
WaitingCount = max( WaitingCount, pCrit->MyLockCount );
if (WaitingCount > 0)
{
LeaveCriticalSection( &pCrit->Crit );
Sleep( 1 );
}
else
{
DeleteCriticalSection( &pCrit->Crit );
}
} while (WaitingCount > 0);
}
}