Внутренне критическая секция представлена записью с несколькими счётчиками, флагами и, возможно, описателем события (заметьте, что внутренняя структура критической секции не документирована и может меняться. Поэтому информация этого поста приводится только для понимания работы и отладки, а не в качестве руководства для написания финального кода). Пока нет никаких разногласий, то достаточно только счетчиков и флагов, потому что никто не должен ждать критическую секцию (и, следовательно, никому не нужно просыпаться, когда критическая секция снова станет доступна).
Если же потоку нужно заблокировать себя, потому что критическая секция, которая ему нужна, занята другим потоком, то ядро создаст событие для критической секции (если это не было сделано ранее) и будет его ожидать. Когда владелец критической секции наконец её отпустит, событие будет возбуждено, уведомляя всех ожидающих о доступности критической секции, так что они смогут попробовать захватить её снова (если критическую секцию ожидает несколько потоков, то её захватит лишь один, а остальные продолжат спать).
Если вы получаете исключение "invalid handle" при вызове
LeaveCriticalSection
, то это означает, что критическая секция посчитала, что её кто-то ждёт и попыталась возбудить событие, но описатель события был испорчен.Теперь вы можете использовать свою голову, чтобы назвать причины для этого.
Одна из возможностей заключается в том, что критическая секция была испорчена. Память, ей занимаемая, была перезаписана какими-то другими данными (которые не являются допустимым описателем события).
Ещё одна возможность: какой-то другой код передал в
CloseHandle
мусор, который случайно оказался равен описателю события критической секции, так что CloseHandle
по ошибке закрыла не тот описатель. Это случается чаще, чем вы думаете. К примеру, из-за бага двойного освобождения: код освобождает описатель, он (описатель) заново используется системой, а затем описатель освобождается снова (из-за ошибки).Конечно же, причиной может быть и то, что критическая секция не была допустимой ещё в момент её захвата (например, она не была инициализирована). Тогда значения внутренних полей будут мусором, который был достаточно похож на правильные данные при входе в секцию, но не содержал допустимого описателя события при попытке выхода.
И, наконец, проблема может заключаться в том, что кто-то уничтожил критическую секцию, пока наш поток её держал. К примеру, поток может содержать такой код:
EnterCriticalSection(cs); ... что-то делаем ... LeaveCriticalSection(cs);И пока этот поток занят "что-то делаем", другой поток вызывает
DeleteCriticalSection(cs)
. Этот вызов уничтожит критическую секцию, пока другой поток ещё использует её. Позже первый поток закончит свои дела и попытается освободить критическую секцию вызовом LeaveCriticalSection
, что приведёт к возбуждению исключения "invalid handle", потому что DeleteCriticalSection
уже закрыла описатель события.Всё это - возможные причины для исключения внутри
LeaveCriticalSection
. Что именно из этого происходит у вас - вам придётся определять отладкой. Но теперь хотя бы вы знаете, что вам нужно искать.P.S.: Мой коллега из команды ядра Windows подсказал, что проверки "Locks and Handles" в программе Application Verifier могут здорово помочь в отладке подобных проблем.
invalid handle появляется при запуске поставщика теневого копирования и поэтому не работает восстановление системы что делать
ОтветитьУдалить