Ваша функция DllMain работает внутри блокировки загрузчика - это один из немногих моментов, когда ОС позволяет вашему коду выполняться в момент удерживания внутренней блокировки. Это означает, что ваш код должен быть очень внимателен, чтобы не нарушить иерархию блокировок в своей DllMain; иначе вам грозит мёртвая блокировка.
(У вас ведь есть такая иерархия, да?).
Блокировка загрузчика ОС требуется для любой функции, которой нужен доступ к списку загруженных в процесс DLL. Это включает в себя такие функции как GetModuleHandle и GetModuleFileName. Если ваша DllMain входит в критическую секцию или ожидает на объекте синхронизации, а эти критическая секция или объект синхронизации принадлежат (owned) какому-то другому потоку, который, в свою очередь, ждёт освобождения блокировки загрузчика, то вы только что создали мёртвую блокировку (deadlock):
// Глобальная переменная
var
csGlobal: TCriticalSection;
// Где-то есть такой код
csGlobal.Enter;
... GetModuleFileName(HInstance, ...);
csGlobal.Leave;
// Эта процедура присваивается DllProc
procedure MyDllMain(dwReason: DWord);
begin
case dwReason of
...
DLL_THREAD_DETACH:
begin
csGlobal.Enter;
...
end;
...
end;
end;
Теперь представьте, что какой-то поток счастливо выполняет первый блок кода и входит в csGlobal, потом управление передайтся ещё кому-то. В это время другой поток завершает свою работу. При этом берётся блокировка загрузчика и рассылается сообщение DLL_THREAD_DETACH (блокировка загрузчика в это время держится). Вы получаете DLL_THREAD_DETACH и пытаетесь войти в csGlobal. Это блокируется первым потоком, который сейчас владеет критической секцией. Потом этот поток продолжает выполнение и вызывает GetModuleFileName. Эта функция требует блокировки загрузчика (поскольку ей нужен доступ к списку DLL, загруженных в процесс), поэтому поток блокируется, потому что блокировкой загрузчика владеет кто-то ещё.
Теперь у вас deadlock:
- csGlobal-ом владеет первый поток, который ждёт блокировки загрузчика.
- Блокировкой загрузчика владеет второй поток, который ждёт csGlobal.
Мораль истории: не забывайте про блокировку загрузчика. Включайте её в свои иерархии блокировок, если вы хотите использовать любые блокировки в своей DllMain.

0 комментарий(ев):
Отправить комментарий
Можно использовать некоторые HTML-теги, например:
<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>
Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку (поддерживается OpenID).
Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.
Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.