Синхронизация. Тихая потеря мютекса
[CODE="cpp"]
if( WaitForSingleObject(rsReadMutex, 0) != WAIT_OBJECT_0 )
ReleaseMutex( rsReadMutex );
WaitForSingleObject( rsReadMutex, 0 );
std::cout << GetLastError() << std::endl;
[/CODE]
Вызов GetLastError возвращает ERROR_NOT_OWNER 288, ну оно и понятно. А вот если убрать всю эту эзотерику, оставив только
[CODE="cpp"]
WaitForSingleObject( rsReadMutex, 0 );
std::cout << GetLastError() << std::endl;
[/CODE]
то GetLastError возвращает 0, что какбы говорит: "мютекс занят, мютекс занят не этим потоком, в остальном - всё ок, т.е. сам дескриптор корректный по-прежнему".
Важно заметить, что никаких других аномалий не наблюдается, никто более не использует мютекс. Приведённый код не просто вырван из контекста, а был отдельно написан для тестирования проблемного участка.
Такая вот <эфемизм к контекстуально уместному слову>:
[CODE="cpp"]
HANDLE rsCollaborativeMutex, rsReadMutex;
DWORD rsReaders;
DWORD WINAPI UseRead( LPVOID )
{
for( ; ; )
{
WaitForSingleObject( rsCollaborativeMutex, INFINITE );
std::cout << "ENTER(" << rsReaders << ", " << (rsReaders+1) << ")\n";
if( ++rsReaders == 1 ) {
std::cout << "SEIZE\n";
WaitForSingleObject( rsReadMutex, INFINITE );
}
std::cout << "GOT(" << rsReaders << ")\n";
ReleaseMutex( rsCollaborativeMutex );
Sleep(10);
WaitForSingleObject( rsCollaborativeMutex, INFINITE );
std::cout << "LEAVE(" << rsReaders << ", " << (rsReaders-1) << ")\n";
if( --rsReaders == 0 ) {
std::cout << "RELEASE\n";
ReleaseMutex( rsReadMutex );
}
std::cout << "GOT(" << rsReaders << ")\n";
ReleaseMutex( rsCollaborativeMutex );
Sleep( 100 );
}
return 0;
}
int main( int argc, char **argv )
{
rsReaders = 0;
rsCollaborativeMutex = CreateMutexA( 0, 0, 0 );
rsReadMutex = CreateMutexA( 0, 0, 0 );
rsPrintMutex = CreateMutexA( 0, 0, 0 );
CreateThread( 0, 0, &UseRead, (LPVOID)70000, 0, 0 );
CreateThread( 0, 0, &UseRead, (LPVOID)30000, 0, 0 );
SuspendThread( GetCurrentThread() );
return 0;
}
[/CODE]
А сама смерть выглядит примерно так:
LEAVE(1, 0)
RELEASE
GOT(0)
ENTER(0, 1)
SEIZE
Следует заметить, что мютекс может быть несколько (и много) раз успешно пройти цикл открития/закрытия, а сплоховать позже. В очередной раз, закрыв мютекс, мы его уже не откроем.
Он не любит захват мутекса в одном, а отпускание в другом потоке.
PS: Так же, он не любит забытые volatile, зато их любит оптимизирующий компилятор.
А что там на счёт
?
Во блин, сам же получил 288. Ходил вокруг да около, но не понял. Видимо спать надо больше. :)
Как заставить полюбить? Можно ли?
Volatile это-то да, но всё это собиралось без оптимизации. Первоначальный код вообще на яз. ассемблера, собирается также без какой-либо оптимизации. Так что это лишнее.
P.S. Да и вообще, volatile не имеет смысла применять к дескриптору, т.к. первый отвечает лишь за запрет "кеширования" значения самой переменной, а не структуры по адресу. А вот к rsReaders в исходном коде помечен у меня volatile, удалил когда форматировал при вставке сюда.
Как заставить полюбить? Можно ли?
А для чего?
Кстати, зачем вы используете мутекс для межпоточной синхронизации? Виндовый мутекс совсем не одно и и тоже что POSIX мутекс.
Но это всё же лучше чем деадлок :)
Msdn ждёт!
А для чего же его еще использовать?
Практически тоже самое.
POSIX