Не то чтобы уж новое и неизвестное, но стиль цепляет, поэтому рискну привести небольшую цитату.
"
Пикник на обочине или не ходите, дети, в DllMain гулять, а то ноги оторвёт
Точка входа в DLL, так же как и точка входа в программу, - это очень специальное место. Зона. В Зоне действуют свои правила касательно того, что можно делать, а что делать нельзя. В Зоне можно инициализировать локальные данные DLL, можно создавать критические секции. В Зоне нельзя динамически загружать другие Модули или создавать потоки. Любой Сталкер знает и следует правилам Зоны. Все остальные рано или поздно нарушают правила и расплачиваются за это.
Что делает Зону особенной? Иные утверждают, что во всем виноват Загрузчик. Загрузчик единственный, кто понимает язык зависимостей между модулями. Он говорит с модулями, загружает их и вызывает из точки входа. Но Загрузчик слаб. Он не в силах совладать с Модулями, нарушающими правила Зоны.
Модули коварны и злы. Они стремятся окружить себя другими Модулями, от которых они зависят. Они любят создавать циклические зависимости между собой. Они загружают другие DLL в ответ на DLL_PROCESS_ATTACH и вызывают функции из непроинициализированных Модулей. Модули пытаются замаскировать и приумножить свое коварство. Они прикрываются статусом “Delay-Loaded DLL” и расставляют ловушки в конструкторах и деструкторах статических объектов.
Это люди сделали их такими. Люди нарушили правила Зоны. И теперь они расплачиваются за это каждый раз, когда идут в Зону."
Экспрессия экспрессией, но проблема действительно существует. И ссылочка http://msdn2.microsoft.com/en-us/library/ms682583.aspx для внимательного чтения просто обязательна.
Причем на "кухонном уровне" наличие гарантированных проблем просто очевидно.
Посыл N1 - должен быть какой-то код (Загрузчик), который в runtime обеспечит загрузку модулей в адресное пространство процесса, настройку ссылок (если это требуется), начальную инициализацию загруженного модуля.
Посыл N2 - в многопоточной среде этот код не может не иметь средств межпоточной синхронизации, вероятнее всего какой-нибудь разновидности giant lock'a (будем реалистами).
Посыл N3 - Загрузчик вынужден отдавать управление внешнему коду (это наш "DllMain") в контексте пользовательского потока. Очевидно, что средства синхронизации при этом остаются захваченными.
C точки зрения Coffman conditions - классическая ситуация для возникновения deadlock'a.
Так что пользоваться чем-либо, кроме функций C-Runtime и маленького набоа WIN32 API внутри DllMain крайне рискованно.
Это кстати, дополнительный повод не создавать сложные глобальные объекты, особенно неизвестного внутреннего устройства (например, такой класс из третьесторонней библиотеки в конструкторе может породить поток или загрузить динамически компонуемую библиотеку).
Кстати, почему пользоваться C-Runtime безопасно?
Если посмотреть на реальную точку входа в созданную DLL, то мы увидим что-то типа _DllMainCRTStartup@12, если не занимались играми с ключами компилятора.
Это собственно и есть точка инициализации runtime - наша функция DllMain будет вызвана уже из нее.
Желающим полной свободы тяжелее - указав свою точку входа, необходимо будет в момент вызова функции с параметром DLL_PROCESS_ATTACH передать управление в CRT_INIT(), иначе нас ждут неприятности.
При выгрузке ситуация аналогичная.
Комментариев нет:
Отправить комментарий