2012-01-12 12 views
6

Tengo un problema con una sección crítica en C++. Estoy recibiendo una ventana de guillotina y cuando yo tiro el proceso puedo ver el hilo que espera en una sección crítica:¿Por qué mi hilo está bloqueado por una sección crítica que no retiene?

16 Id: b10.b88 Suspend: 1 Teb: 7ffae000 Unfrozen 
ChildEBP RetAddr 
0470f158 7c90df3c ntdll!KiFastSystemCallRet 
0470f15c 7c91b22b ntdll!NtWaitForSingleObject+0xc 
0470f1e4 7c901046 ntdll!RtlpWaitForCriticalSection+0x132 
0470f1ec 0415647e ntdll!RtlEnterCriticalSection+0x46 

Los datos de la línea, etc, todo indica que la entrada en una sección crítica específica. El único problema es que no hay otros subprocesos que mantengan abierta esta sección crítica. El comando de bloqueos de Windbg no indica nada y la sección crítica indica que no está bloqueado, como puede ver el propietario nulo y -1 LockCount en la estructura siguiente.

0:016> dt _RTL_CRITICAL_SECTION 42c2318 
_RTL_CRITICAL_SECTION 
    +0x000 DebugInfo  : 0x02c8b318 _RTL_CRITICAL_SECTION_DEBUG 
    +0x004 LockCount  : -1 
    +0x008 RecursionCount : -1 
    +0x00c OwningThread  : (null) 
    +0x010 LockSemaphore : 0x00000340 
    +0x014 SpinCount  : 0 

0:016> dt _RTL_CRITICAL_SECTION_DEBUG 2c8b318 
_RTL_CRITICAL_SECTION_DEBUG 
    +0x000 Type    : 0 
    +0x002 CreatorBackTraceIndex : 0x2911 
    +0x004 CriticalSection : 0x042c2318 _RTL_CRITICAL_SECTION 
    +0x008 ProcessLocksList : _LIST_ENTRY [ 0x2c8b358 - 0x2c8b2e8 ] 
    +0x010 EntryCount  : 1 
    +0x014 ContentionCount : 1 
    +0x018 Flags   : 0xbaadf00d 
    +0x01c CreatorBackTraceIndexHigh : 0xf00d 
    +0x01e SpareWORD  : 0xbaad 

¿Cómo es esto posible? Incluso en un punto muerto donde otro hilo no ha llamado a LeaveCriticalSection esperaría ver la sección crítica marcada como bloqueada. ¿Alguien tiene alguna sugerencia de depuración o posibles soluciones?

+1

Una cosa que verificaría es si he hecho una sola EnterCriticalSection seguida de 2 LeaveCriticalSections. – Naveen

+0

Compruebe que la sección crítica no se haya eliminado. De [DeleteCriticalSection] (http://msdn.microsoft.com/en-us/library/windows/desktop/ms682552%28v=vs.85%29.aspx): si se elimina una sección crítica mientras todavía es propiedad, el estado de los hilos que esperan la propiedad de la sección crítica eliminada no está definido. – hmjd

+0

@hmjd probablemente es correcto mead 0xbaadf00d que se realizó la desasignación. – Zuljin

Respuesta

8

Resultó ser un error donde se llamaba a LeaveCriticalSection sin una EnterCriticalSection correspondiente. Esto hizo que la sección crítica para decrementar cuenta de bloqueos y RecursionCount en el estado siguiente (el valor predeterminado para cuenta de bloqueos es -1 y RecursionCount es 0):

0:016> dt _RTL_CRITICAL_SECTION 1092318 
_RTL_CRITICAL_SECTION 
    +0x000 DebugInfo  : 0x....... _RTL_CRITICAL_SECTION_DEBUG 
    +0x004 LockCount  : -2 
    +0x008 RecursionCount : -1 
    +0x00c OwningThread  : (null) 
    +0x010 LockSemaphore : 0x....... 
    +0x014 SpinCount  : 0 

Cuando se realizó la EnterCriticalSection posterior, se colgaron porque RecursionCount era distinto de cero - un hilo solo puede tomar posesión de la sección crítica si RecursionCount es 0. Sin embargo, incrementó LockCount (llevándolo al -1 visto en mi pregunta original) solo para confundir las cosas.

En resumen, si ve una sección crítica que detiene el hilo con LockCount y RecursionCount de -1, significa que hubo un desbloqueo excesivo.

En cuanto al código que lo causa:

if (SysStringLen(bstrState) > 0) 
    CHECKHR_CS(m_pStateManager->SetState(bstrState), &m_csStateManagerLock); 

Y la definición de la comprobación de errores de macro:

#define CHECKHR_CS(x, cs)      \ 
    EnterCriticalSection(cs);      \ 
    if(FAILED(hr = (x))) {      \ 
     LeaveCriticalSection(cs);     \ 
     return hr;       \ 
    }       \ 
    LeaveCriticalSection(cs); 

La macro carece de llaves de todo su contenido, por lo que la sentencia if no siendo satisfecho solo omite EnterCriticalSection. Obviamente un problema.

+3

Considere usar [el modismo RAII] (http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization) y envuelva 'EnterCriticalSection()' en un constructor y 'LeaveCriticalSection()' en un destructor. De esta forma, no olvidará desbloquear el mutex (o desbloquearlo dos veces). Así es como funciona [lock_guard'] de Boost (http://www.boost.org/doc/libs/1_48_0/doc/html/thread/synchronization.html#thread.synchronization.locks.lock_guard). –

+0

Buen punto. Hubiera arrojado un error de compilación para una variable no declarada si se usara aquí. – dlanod

Cuestiones relacionadas