Продолжение истории про ExitProcess.

История про ExitProcess получила несколько неожиданное продолжение. Оказалось, что для того, чтобы избежать блокировки на критической секции, захваченной другим потоком во время ExitProcess, в функцию EnterCriticalSection был добавлен код, обрабатывающий эту ситуацию. Начиная с Windows XP EnterCriticalSection проверяет захвачена ли секция и, в случае если захвачена, сверяет идентификатор текущего потока с идентификатором захватчика. А вот дальнейшее поведение зависит от версии операционной системы.

В случае Windows XP и, я подозреваю, Windows 2003 – всё происходит так, как описал Raimond Chen (спасибо Константину за ссылку). Вместо бесконечного ожидания, текущий поток назначается владельцем критической секции, так как если бы она была захвачена нормальным способом. Тем самым программа сможет продолжить выполнение вплоть до финального вызова NtTerminateProcess.

Позднее такое решение проблемы оказалось недостаточно безопасным. Ведь фактически неосвобожденная критическая секция означает, что структуры данных которые она защищала, скорее всего повреждены, а указатели могут указывать в никуда. Повторное обращение с большой вероятностью может вызвать исключение. В результате в Windows Vista вместо имитации успешного захвата секции сразу вызывается NtTerminateProcess.

Но и это ещё не всё. В случае, если завершаемый процесс выполняется в Wow64 (32-х битный процесс, выполняющийся на 64-х разрядной системе), то приложение всё равно может повиснуть при выполнении ExitProcess. Причина тут в том, что wow64.dll сама использует критические секции, которые могут быть задействованы при отработке DLL_PROCESS_DETACH, поскольку флаги, выставляемые 32-х битным ExitProcess, не видны из 64-х разрядной версии EnterCriticalSection. Но это уже чистый баг, который уже почти исправлен. :-)

comments powered by Disqus