// стека для __LINE__ и __FILE__ сделает нашу систему
// более громоздкой. Если это действительно необходимо,
// Вы всегда можете сделать это самостоятельно
pcs->RecursionCount++;
return;
}
// Критическая секция занята другой нитью.
// Придется подождать
_WaitForCriticalSectionDbg(pcs, nLine, azFile);
}
// Либо критическая секция была "свободна",
// либо мы дождались. Сохраняем идентификатор текущей нити.
pcs->OwningThread = (HANDLE)::GetCurrentThreadId();
pcs->RecursionCount = 1;
pcs->m_nLine = nLine;
pcs->m_azFile = azFile;
}
// Заполучаем критическую секцию если она никем не занята
inline BOOL TryEnterCriticalSectionDbg(LPCRITICAL_SECTION_DBG pcs, int nLine, LPSTR azFile) {
if (-1L == ::InterlockedCompareExchange(&pcs->LockCount, 0, -1)) {
// Это первое обращение к критической секции
pcs->OwningThread = (HANDLE)::GetCurrentThreadId();
pcs->RecursionCount = 1;
pcs->m_nLine = nLine;
pcs->m_azFile = azFile;
} else if (pcs->OwningThread == (HANDLE)::GetCurrentThreadId()) {
// Это не первое обращение, но из той же нити
::InterlockedIncrement(&pcs->LockCount);
pcs->RecursionCount++;
} else return FALSE; // Критическая секция занята другой нитью
return TRUE;
}
// Освобождаем критическую секцию
inline VOID LeaveCriticalSectionDbg(LPCRITICAL_SECTION_DBG pcs) {
// Проверяем, чтобы идентификатор текущей нити совпадал
// с идентификатором нити-влядельца.
// Если это не так, скорее всего мы имеем дело с ошибкой
ATLASSERT(pcs->OwningThread == (HANDLE)::GetCurrentThreadId());
if (--pcs->RecursionCount) {
// Не последний вызов из этой нити.
// Уменьшаем значение поля LockCount
::InterlockedDecrement(&pcs->LockCount);
} else {
// Последний вызов. Нужно "разбудить" какую-либо
// из ожидающих ниток, если таковые имеются
ATLASSERT(NULL != pcs->OwningThread);
pcs->OwningThread = NULL;
pcs->m_nLine = 0;
pcs->m_azFile = NULL;
if (::InterlockedDecrement(&pcs->LockCount) >= 0) {
// Имеется, как минимум, одна ожидающая нить
_UnWaitCriticalSectionDbg(pcs);
}
}
}
// Удостоверяемся, что ::EnterCriticalSection() была вызвана
// до вызова этого метода
inline BOOL CheckCriticalSection(LPCRITICAL_SECTION pcs) {
return pcs->LockCount >= 0
&& pcs->OwningThread == (HANDLE)::GetCurrentThreadId();
}
// Переопределяем все функции для работы с критическими секциями.
// Определение класса CLock должно быть после этих строк
#define InitializeCriticalSection InitializeCriticalSectionDbg
#define InitializeCriticalSectionAndSpinCount(pcs, c) \
InitializeCriticalSectionDbg(pcs)
#define DeleteCriticalSection DeleteCriticalSectionDbg
#define EnterCriticalSection(pcs) EnterCriticalSectionDbg(pcs, __LINE__, __FILE__)
#define TryEnterCriticalSection(pcs) TryEnterCriticalSectionDbg(pcs, __LINE__, __FILE__)
#define LeaveCriticalSection LeaveCriticalSectionDbg
#define CRITICAL_SECTION CRITICAL_SECTION_DBG
#define LPCRITICAL_SECTION LPCRITICAL_SECTION_DBG
#define PCRITICAL_SECTION PCRITICAL_SECTION_DBG
#endif
Приводим наши классы в соответствие
Листинг 17. Классы CLock и CScopeLock, вариант для отладки
class CLock {
friend class CScopeLock;
CRITICAL_SECTION m_CS;
public:
void Init() { ::InitializeCriticalSection(&m_CS); }
void Term() { ::DeleteCriticalSection(&m_CS); }
#if defined(CS_DEBUG)
BOOL Check() { return CheckCriticalSection(&m_CS); }
#endif
#if CS_DEBUG > 1
void Lock(int nLine, LPSTR azFile) {
EnterCriticalSectionDbg(&m_CS, nLine, azFile);
}
BOOL TryLock(int nLine, LPSTR azFile) {
return TryEnterCriticalSectionDbg(&m_CS, nLine, azFile);
}
#else
void Lock() {
::EnterCriticalSection(&m_CS);
}
BOOL TryLock() {
return ::TryEnterCriticalSection(&m_CS);
}
#endif
void Unlock() {
::LeaveCriticalSection(&m_CS);
}
};
class CScopeLock {
LPCRITICAL_SECTION m_pCS;
public:
#if CS_DEBUG > 1
CScopeLock(LPCRITICAL_SECTION pCS, int nLine, LPSTR azFile) : m_pCS(pCS) {
Lock(nLine, azFile);
}
CScopeLock(CLock& lock, int nLine, LPSTR azFile) : m_pCS(&lock.m_CS) {
Lock(nLine, azFile);
}
void Lock(int nLine, LPSTR azFile) {
EnterCriticalSectionDbg(m_pCS, nLine, azFile);
}
#else
CScopeLock(LPCRITICAL_SECTION pCS) : m_pCS(pCS) { Lock(); }
CScopeLock(CLock& lock) : m_pCS(&lock.m_CS) { Lock(); }
void Lock() { ::EnterCriticalSection(m_pCS); }
#endif
~CScopeLock() { Unlock(); }
void Unlock() { ::LeaveCriticalSection(m_pCS); }
};
#if CS_DEBUG > 1
#define Lock() Lock(__LINE__, __FILE__)
#define TryLock() TryLock(__LINE__, __FILE__)
#define lock(cs) lock(cs, __LINE__, __FILE__)
#endif
К сожалению, пришлось даже переопределить CScopeLock lock(cs), причем мы жестко привязались к имени переменной. Не говоря уж о том, что у нас наверняка получился конфликт имен, все-таки Lockдовольно популярное название для метода. Такой код не будет собираться, например, с популярнейшей библиотекой ATL. Тут есть два способа. Переименовать наши методы Lock() и TryLock() во что-нибудь более уникальное либо переименовать Lock() в ATL:
Читать дальше