Начало » Использование СУБД » Firebird, HQbird, InterBase » Параметры firebird_udr_plugin (Хочу все знать!)
Параметры firebird_udr_plugin [сообщение #2229] |
Mon, 15 May 2023 17:19 |
shalamyansky
Сообщений: 150 Зарегистрирован: August 2022
|
Senior Member |
|
|
Вопрос прежде всего к разработчикам движка udr_engine, если они сюда заглядывают, а также к Денису Симонову, как человеку, глубоко разобравшемуся с использованием этого движка.
Вот так описана точка входа в модуль udr
Показать скрытый текст
UdrCppEngine.h
FB_BOOLEAN* FB_UDR_PLUGIN_ENTRY_POINT(::Firebird::IStatus* status, FB_BOOLEAN* theirUnloadFlag, ::Firebird::IUdrPlugin* udrPlugin);
Интересует смысл и предполагаемое правильное использование параметра theirUnloadFlag, а также возвращаемого значения (далее в коде именуемого как myUnloadFlag).
Файл UdrCppEngine.h предлагает шаблон реализации точки входа:
Показать скрытый текст
UdrCppEngine.h
extern "C" FB_DLL_EXPORT FB_BOOLEAN* FB_UDR_PLUGIN_ENTRY_POINT(::Firebird::IStatus* status, \
FB_BOOLEAN* theirUnloadFlag, ::Firebird::IUdrPlugin* udrPlugin) \
{ \
::Firebird::Udr::FactoryRegistration::finish(status, udrPlugin); \
\
class UnloadDetector \
{ \
public: \
UnloadDetector(FB_BOOLEAN* aTheirUnloadFlag, ::Firebird::IUdrPlugin* aUdrPlugin) \
: myUnloadFlag(FB_FALSE), \
theirUnloadFlag(aTheirUnloadFlag), \
udrPlugin(aUdrPlugin) \
{ \
} \
\
~UnloadDetector() \
{ \
if (!myUnloadFlag) \
*theirUnloadFlag = FB_TRUE; \
} \
\
FB_BOOLEAN myUnloadFlag; \
FB_BOOLEAN* theirUnloadFlag; \
::Firebird::IUdrPlugin* udrPlugin; \
}; \
\
static UnloadDetector unloadDetector(theirUnloadFlag, udrPlugin); \
\
return &unloadDetector.myUnloadFlag; \
}
Такую же логику старта и завершения повторяет и Денис в своей статье, посвященной созданию UDR на Pascal, а также, вероятно, и прочие писатели UDR, к коим примкнул и автор этих строк.
Повторить-то я повторил, но смысл не понял. Что означают оба этих флага, кто когда какой флаг может и должен читать и кто когда какой флаг должен устанавливать? Это оповещение о выгрузке, разрешение выгрузки, запрет выгрузки? От кого кому?
В файле UdrEngine.cpp можно увидеть события со стороны движка, но понимания мне это не добавило:
Показать скрытый текст
UdrEngine.cpp
UdrPluginImpl* Engine::loadModule(ThrowStatusWrapper* status, IRoutineMetadata* metadata, PathName* moduleName, string* entryPoint)
{
...
UdrPluginImpl* udrPlugin = FB_NEW UdrPluginImpl(*moduleName, module);
udrPlugin->theirUnloadFlag = entryPoint(status, &udrPlugin->myUnloadFlag, udrPlugin);
...
...
}
class UdrPluginImpl : public VersionedIface<IUdrPluginImpl<UdrPluginImpl, ThrowStatusWrapper> >
{
public:
UdrPluginImpl(const PathName& aModuleName, ModuleLoader::Module* aModule)
: moduleName(*getDefaultMemoryPool(), aModuleName),
module(aModule),
myUnloadFlag(FB_FALSE),
theirUnloadFlag(NULL),
functionsMap(*getDefaultMemoryPool()),
proceduresMap(*getDefaultMemoryPool()),
triggersMap(*getDefaultMemoryPool())
{
}
~UdrPluginImpl()
{
if (myUnloadFlag)
return;
*theirUnloadFlag = FB_TRUE;
{
GenericMap<Pair<Left<string, IUdrFunctionFactory*> > >::Accessor accessor(&functionsMap);
for (bool cont = accessor.getFirst(); cont; cont = accessor.getNext())
accessor.current()->second->dispose();
}
{
GenericMap<Pair<Left<string, IUdrProcedureFactory*> > >::Accessor accessor(&proceduresMap);
for (bool cont = accessor.getFirst(); cont; cont = accessor.getNext())
accessor.current()->second->dispose();
}
{
GenericMap<Pair<Left<string, IUdrTriggerFactory*> > >::Accessor accessor(&triggersMap);
for (bool cont = accessor.getFirst(); cont; cont = accessor.getNext())
accessor.current()->second->dispose();
}
}
FB_BOOLEAN myUnloadFlag;
FB_BOOLEAN* theirUnloadFlag;
};
Если кто паче чаяния решит вникнуть, имейте в виду, что флаги myUnloadFlag и theirUnloadFlag со стороны движка и модуля имеют обратный смысл: то, что в файле UdrEngine.cpp названо myUnloadFlag, в файле UdrCppEngine.h называется theirUnloadFlag, и наоборот. Техника именования очень способствует тренировке мозгов по распутыванию сложных зависимостей.
Более означенные флаги нигде не используются. Вся логика сокрыта (открыта) в приведенных фрагментах, но я её не понимаю. Прошу помощи.
Исходные тексты взяты из Firebird-4.0.2.2816-0/src.
|
|
|
Re: Параметры firebird_udr_plugin [сообщение #2230 является ответом на сообщение #2229] |
Mon, 15 May 2023 18:19 |
shalamyansky
Сообщений: 150 Зарегистрирован: August 2022
|
Senior Member |
|
|
Поясню, зачем мне эти знания понадобились.
С некоторых пор сервер стал спорадически отваливаться по AccessViolation (Exception code=0xc0000005). Изучение журналов системы, трассировки и дампов привело к пониманию, что нехорошие события происходят в момент выгрузки моих модулей UDR и udr_engine.dll, ровно через минуту после закрытия коннекта, использовавшего мой модуль UDR. Иногда об AV спотыкается код, находящийся в udr_engine.dll, иногда находящийся в oleaut32.dll. Библиотеку oleaut32.dll Firebird сам по себе не грузит, она загружается вместе с моим модулем UDR и выгружается вместе с ним. На самом деле она для дела не нужна, но избавиться от зависимости трудно, пока не сумел. Но это отдельный вопрос, связанный с особенностями Delphi.
Так вот, очень-очень похоже, что бяка связана с кодом финализации и выгрузки движка и модулей. Код, приведенный в топике, показывает, что, например, в этот момент происходит попытка записи одного байта по переданному некогда ранее указателю, то есть "где-то там", в сегменте данных другой dll. Причем такое происходит дважды, сперва udr_engine пишет по адресу *theirUnloadFlag, а потом аналогично модуль UDR внутри себя делает такую же операцию. Если порядок выгрузки меняется, что-то выгружается дважды и т.п., могут быть проблемы.
Это только как гипотеза. Чтобы увериться, что флаги Unload тут не причем, собственно и задан вопрос топика.
|
|
|
|
|
|
|
Re: Параметры firebird_udr_plugin [сообщение #2236 является ответом на сообщение #2233] |
Mon, 15 May 2023 19:03 |
shalamyansky
Сообщений: 150 Зарегистрирован: August 2022
|
Senior Member |
|
|
sim_84 писал(а) Mon, 15 May 2023 18:52 всякую хреномуть с ole не советую использовать в udr
Чур меня, чур! Чураюсь ole, как черт ладана. Однако ж в Delphi в Classes воткнута зависимость от ActiveX, и никак от нее избавиться. Избавиться от Classes тоже пока не получилось, это TStrings и прочее, что так любят зачем-то без особой нужды использовать разработчики. В частности, разработчики порта PCRE в Delphi RegularExpressions.
Цитата:
Приходи на конференцию
Спасибо за приглашение, я записан!
Поговорим, а то!
[Обновления: Mon, 15 May 2023 19:05] Известить модератора
|
|
|
Re: Параметры firebird_udr_plugin [сообщение #2237 является ответом на сообщение #2229] |
Mon, 15 May 2023 23:16 |
hvlad
Сообщений: 357 Зарегистрирован: August 2022
|
Senior Member |
|
|
shalamyanskyИнтересует смысл и предполагаемое правильное использование параметра theirUnloadFlag, а также возвращаемого значения (далее в коде именуемого как myUnloadFlag). Я могу попробовать объяснить смысл, но не буду утверждать, что там всё правильно сделано и всё предусмотрено.
Итак, UDR - это динамически загружаемый модуль (DLL или SO). Речь идёт о контроле за выгрузкой этого модуля.
Точнее - об корректной процедуре очистке модулей в процессе завершения их работы.
В штатном случае, когда хорошее приложение вызывает fb_shutdown() перед тем как покинуть OS,
выгрузкой внешних (по отношению к движку) модулей управляет Plugin Manager, который реализован
в fbclient.dll. Он выгружает (в том числе) udr_engine, который в свою очередь выгружает загруженные
им самим модули с юзерскими UDR. Т.е. udr_engine выступает в роли Plugin Manager для модулей с UDR.
Заметим, что тут присутствует правильный порядок выгрузки модулей и поэтому не возможна ситуация,
когда вызывается код из уже выгруженного модуля
Если же приложение НЕ хорошее, и не вызывает fb_shutdown(), то выгрузкой всех модулей заведует OS.
И нет никакого определённого гарантированного порядка в котором она будет выгружать модули.
Поэтому вполне возможна ситуация, когда, например, udr_engine в процессе очистки вызовет метод из
уже выгруженного модуля UDR.
Для того, чтобы этого избежать, используется набор флагов выгрузки: в udr_engine и в каждой UDR.
При загрузке мождуля UDR, udr_engine передаёт в UDR адрес своего флага выгрузки и получает адрес
флага выгрузки от UDR. Изначально все флаги установлены в FB_FALSE.
Если udr_engine выгружается первым (т.е. в правильном порядке), то он всем загруженным модулям UDR
устанавливает значения их флагов в FB_TRUE и далее выполняет свою очистку как обычно. Если UDR
имеет свой код очистки (в DllMain(), деструкторы статических объектов, секции finalization в Pascal),
то такой код может (и даже должен) ориентироваться на этот флаг при необходимости вызова методов FB API.
Если флаг == FB_FALSE, то вызывать FB API опасно (в коде выгрузки модуля, есс-но, а не всегда )
Лучше всего такого кода не иметь вовсе.
Когда UDR выгружается, она может по состоянию своего флага понять - её выгружает udr_engine (флаг ==
FB_TRUE), или же это нештатная выгрузка (флаг остался FB_FALSE). Во втором случае UDR присваивает
значение FB_TRUE флагу udr_engine'а - это сигнал, что что-то идёт не так.
Теперь, когда очередь выгружаться дойдёт до udr_engine, его флаг будет FB_TRUE и он не станет
выполнять свою очистку и выгружать UDR - это опасно, т.к. какие-то из них уже выгружены.
Надеюсь, стало понятнее.
|
|
|
Re: Параметры firebird_udr_plugin [сообщение #2238 является ответом на сообщение #2237] |
Tue, 16 May 2023 00:34 |
SD
Сообщений: 411 Зарегистрирован: August 2022
|
Senior Member |
|
|
Твои слова да богу б в уши...
Этот самый plugin manager, может, и не вызовет код выгруженной DLL. Но он со спокойной совестью его вызовет до выгрузки, но уже после того, как отработали деструкторы глобальных переменных это DLL. А если эта DLL порождает внутри потоки, то... проще её принудительно самозаблокировать в памяти до полного завершения, увеличив счётчик ссылок, чем надеяться на неестественный интеллект менеджера.
А OS выгружает библиотеки не просто в предсказуемом, но даже документированном порядке, обратном дереву их загрузки.
И да, не так давно именно с выгрузкой udr_engine правился (Алексом, кажется) баг. Так что обновляться до снапшота очень рекомендую.
|
|
|
|
Re: Параметры firebird_udr_plugin [сообщение #2242 является ответом на сообщение #2241] |
Tue, 16 May 2023 14:14 |
shalamyansky
Сообщений: 150 Зарегистрирован: August 2022
|
Senior Member |
|
|
Спасибо большое, Влад!
Честно говоря, я и сам уже путем мощного умственного напряжения почти дошел до понимания этой схемы, но слово разработчика всегда точнее, полнее и, главное, достовернее.
Денис, мне кажется, текст Влада полезно вставить в вашу статью по написанию UDR. Мы-то разобрались с этим вопросом, а вот будущим читателям будет очень на пользу, вопрос-то возникает, да и нам тоже для освежения памяти. Может, какой раздел FAQ, вам виднее.
|
|
|
Re: Параметры firebird_udr_plugin [сообщение #2243 является ответом на сообщение #2241] |
Tue, 16 May 2023 14:28 |
SD
Сообщений: 411 Зарегистрирован: August 2022
|
Senior Member |
|
|
hvlad писал(а) Tue, 16 May 2023 11:12С чего бы этим деструкторам отрабатывать до выгрузки DLL ?
Не "до", а "во время" выгрузки. И это именно то время, когда менеджер вызывает, например, doClean() (но он не вызывает shutdown()) когда через минуту после закрытия последнего коннекта выгружает движок, а тот, соответственно, выгружает загруженный в него udr_engine вместе со всеми UDR. И вот этот процесс, в отличии от системного, совершенно непредсказуем из-за спагетти-кода, многократно обёртутого вокруг единственного плагин менеджера (а если не повезло, то и нескольких).
И, кстати, о том, что fb_shutdown (в виде IProvider::shutdown) до тех же провайдеров никак не доходит, я тоже писал в девеле.
|
|
|
Re: Параметры firebird_udr_plugin [сообщение #2244 является ответом на сообщение #2243] |
Tue, 16 May 2023 14:45 |
hvlad
Сообщений: 357 Зарегистрирован: August 2022
|
Senior Member |
|
|
SD писал(а) Tue, 16 May 2023 14:28hvlad писал(а) Tue, 16 May 2023 11:12С чего бы этим деструкторам отрабатывать до выгрузки DLL ?
Не "до", а "во время" выгрузки. Во время штатной выгрузки нет проблем. По крайней мере описанных мною.
SDИ это именно то время, когда менеджер вызывает, например, doClean() (но он не вызывает shutdown()) когда через минуту после закрытия последнего коннекта выгружает движок, а тот, соответственно, выгружает загруженный в него udr_engine вместе со всеми UDR. И вот этот процесс, в отличии от системного, совершенно непредсказуем из-за спагетти-кода, многократно обёртутого вокруг единственного плагин менеджера (а если не повезло, то и нескольких). Поток сознания не комментирую, нет смысла. Есть стек с конкретной проблемой ?
SDИ, кстати, о том, что fb_shutdown (в виде IProvider::shutdown) до тех же провайдеров никак не доходит, я тоже писал в девеле. Тебе сказали, что сделать, ты забил т.к. оно тебе не надо.
Всё как обычно.
|
|
|
Re: Параметры firebird_udr_plugin [сообщение #2252 является ответом на сообщение #2236] |
Tue, 16 May 2023 19:17 |
shalamyansky
Сообщений: 150 Зарегистрирован: August 2022
|
Senior Member |
|
|
shalamyansky писал(а) Mon, 15 May 2023 19:03Однако ж в Delphi в Classes воткнута зависимость от ActiveX, и никак от нее избавиться. Избавиться от Classes тоже пока не получилось, это TStrings и прочее, что так любят зачем-то без особой нужды использовать разработчики.
В продолжение Мерлезонского балета скажу, что от указанных зависимостей я таки ушел. Пришлось сделать свою микро версию Classes. Как результат oleaut32.dll загружаться/выгружаться не должна. Вряд ли именно в этом корень насущной проблемы, но все равно приятно. Будем смотреть дальше.
P.S.
Поспешил я, однако. От ActiveX освободился, но не от oleaut32. Та, оказывается, вообще в System зашита. Если у вас MSWINDOWS, то будьте любезны, примите и пр. Не отвертишься. Печаль.
[Обновления: Tue, 16 May 2023 22:06] Известить модератора
|
|
|
|
|
Re: Параметры firebird_udr_plugin [сообщение #2267 является ответом на сообщение #2264] |
Wed, 17 May 2023 14:31 |
SD
Сообщений: 411 Зарегистрирован: August 2022
|
Senior Member |
|
|
hvlad писал(а) Wed, 17 May 2023 08:20
Не надоело воду в ступе толочь ? Давай конкретный кейс с конкретной проблемой - будем смотреть.
И, да, те кто не выполняет рекомендации - сам себе сам знаешь кто.
Напоминаю, это isql. Штатная утилита Firebird. Кто для него сам знаешь кто?..
А если хочешь кейс, его есть у меня:
1. Идёшь на http://fireswarm.ddns.net и скачиваешь подходящий тебе debug build.
2. Устанавливаешь согласно инструкции там же.
3. Запускаешь isql, подключаешься опять же согласно инструкции.
4. Выходишь из isql любым правильным способом.
5. Открываешь в c:\ProgramData\fireswarm\logs файл лога (который появится если у тебя достаточно прав на запись туда) и читаешь, начиная с конца, в поисках заветной строки "Not implemented ClientProvider::shutdown". Найдёшь её - сообщи. Как я ни пробовал, ни isql, ни IBExpert, ни FlameRobin не смогли завершиться так, чтобы её получить.
PS: Строка "Log destruction!!!" в логе сигнализирует срабатывание деструкторов глобальных переменных.
PPS: Три восклицательных знака в ней - результат многих часов отладки крэша, сделавших меня намного умнее и эмоциональнее.
|
|
|
|
|
|
Re: Параметры firebird_udr_plugin [сообщение #2271 является ответом на сообщение #2270] |
Wed, 17 May 2023 17:20 |
SD
Сообщений: 411 Зарегистрирован: August 2022
|
Senior Member |
|
|
Минимальный плагин - по ссылке выше.
"При выгрузке провайдера из isql IPluginModule::doClean() вызывается после деструкторов, а IProvider::shutdown() не вызывается вообще" по-моему уже конкретизировать дальше некуда. Не хочешь использовать лог от моего плагина - ткни три брейкпоинта в названные точки любого кошерного.
И таки да, я за собственное эго, ибо с надеждами на "хорошее и правильное" давно распрощался, поскольку работать приходится с реальностью, данной в очучениях, а в этой реальности ни одно из вышеназванных приложений не делает мифическую "штатную выгрузку", так что приходится глобалы либо ликвидировать вообще, либо обкладывать флагами "уничтожен, не трогать".
|
|
|
Re: Параметры firebird_udr_plugin [сообщение #2272 является ответом на сообщение #2271] |
Wed, 17 May 2023 19:22 |
hvlad
Сообщений: 357 Зарегистрирован: August 2022
|
Senior Member |
|
|
SD писал(а) Wed, 17 May 2023 17:20Минимальный плагин - по ссылке выше.
"При выгрузке провайдера из isql IPluginModule::doClean() вызывается после деструкторов, а IProvider::shutdown() не вызывается вообще" по-моему уже конкретизировать дальше некуда. Не хочешь использовать лог от моего плагина - ткни три брейкпоинта в названные точки любого кошерного.
В порядке срабатывания
1. IProvider::shutdown()
> engine13.dll!Jrd::JProvider::shutdown(status=0x000000000014ef28, timeout=0, reason=-3) Line 4600 C++
engine13.dll!Firebird::IProviderBaseImpl<...>::cloopshutdownDispatcher(self=0x00000000005236d8, status=0x000000000014f570, timeout=0, reason=-3) Line 12140 C++
fbclient.dll!Firebird::IProvider::shutdown<Firebird::CheckStatusWrapper>(status=0x000000000014f568, timeout=0, reason=-3) Line 2939 C++
fbclient.dll!Why::Dispatcher::shutdown(userStatus=0x000000000014fa88, timeout=0, reason=-3) Line 6678 C++
fbclient.dll!fb_shutdown(timeout=0, reason=-3) Line 3791 C++
isql.exe!atexit_fb_shutdown() Line 1806 C++
2. IPluginModule::doClean()
> engine13.dll!Firebird::UnloadDetectorHelper::doClean() Line 345 C++
engine13.dll!Firebird::IPluginModuleBaseImpl<Firebird::UnloadDetectorHelper,...>::cloopdoCleanDispatcher(self=0x0000000000522ad8) Line 8474 C++
fbclient.dll!Firebird::IPluginModule::doClean() Line 825 C++
fbclient.dll!`anonymous namespace'::PluginModule::~PluginModule() Line 406 C++
[External Code]
fbclient.dll!Firebird::RefCounted::release() Line 47 C++
fbclient.dll!Firebird::RefPtr<`anonymous namespace'::PluginModule>::~RefPtr<`anonymous namespace'::PluginModule>() Line 135 C++
fbclient.dll!`anonymous namespace'::ConfiguredPlugin::~ConfiguredPlugin() Line 518 C++
[External Code]
fbclient.dll!`anonymous namespace'::ConfiguredPlugin::release() Line 717 C++
fbclient.dll!Firebird::RefPtr<`anonymous namespace'::ConfiguredPlugin>::assign(p=0x0000000000000000) Line 238 C++
fbclient.dll!Firebird::RefPtr<`anonymous namespace'::ConfiguredPlugin>::operator=(p=0x0000000000000000) Line 166 C++
fbclient.dll!`anonymous namespace'::PluginSet::next(status=0x000000000014eef8) Line 867 C++
fbclient.dll!Firebird::IPluginSetBaseImpl<...>::cloopnextDispatcher(self=0x00000000001c7de8, status=0x000000000014f3a8) Line 7811 C++
fbclient.dll!Firebird::IPluginSet::next<Firebird::CheckStatusWrapper>(status=0x000000000014f3a0) Line 523 C++
fbclient.dll!Firebird::GetPlugins<Firebird::IProvider>::next() Line 108 C++
fbclient.dll!Why::Dispatcher::shutdown(userStatus=0x000000000014fa88, timeout=0, reason=-3) Line 6669 C++
fbclient.dll!fb_shutdown(timeout=0, reason=-3) Line 3791 C++
isql.exe!atexit_fb_shutdown() Line 1806 C++
[External Code]
3. Деструктор глоб. переменной в jrd.cpp
> engine13.dll!Jrd::Foo::~Foo() Line 464 C++
[External Code]
fbclient.dll!Win32Module::~Win32Module() Line 257 C++
[External Code]
fbclient.dll!Firebird::SimpleDelete<ModuleLoader::Module>::clear(ptr=0x00000000001c7fc0) Line 47 C++
fbclient.dll!Firebird::AutoPtr<ModuleLoader::Module,Firebird::SimpleDelete>::~AutoPtr<ModuleLoader::Module,Firebird::SimpleDelete>() Line 118 C++
fbclient.dll!`anonymous namespace'::PluginModule::~PluginModule() Line 407 C++
[External Code]
fbclient.dll!Firebird::RefCounted::release() Line 47 C++
fbclient.dll!Firebird::RefPtr<`anonymous namespace'::PluginModule>::~RefPtr<`anonymous namespace'::PluginModule>() Line 135 C++
fbclient.dll!`anonymous namespace'::ConfiguredPlugin::~ConfiguredPlugin() Line 518 C++
[External Code]
fbclient.dll!`anonymous namespace'::ConfiguredPlugin::release() Line 717 C++
fbclient.dll!Firebird::RefPtr<`anonymous namespace'::ConfiguredPlugin>::assign(p=0x0000000000000000) Line 238 C++
fbclient.dll!Firebird::RefPtr<`anonymous namespace'::ConfiguredPlugin>::operator=(p=0x0000000000000000) Line 166 C++
fbclient.dll!`anonymous namespace'::PluginSet::next(status=0x000000000014eef8) Line 867 C++
fbclient.dll!Firebird::IPluginSetBaseImpl<...>::cloopnextDispatcher(self=0x00000000001c7de8, status=0x000000000014f3a8) Line 7811 C++
fbclient.dll!Firebird::IPluginSet::next<Firebird::CheckStatusWrapper>(status=0x000000000014f3a0) Line 523 C++
fbclient.dll!Firebird::GetPlugins<Firebird::IProvider>::next() Line 108 C++
fbclient.dll!Why::Dispatcher::shutdown(userStatus=0x000000000014fa88, timeout=0, reason=-3) Line 6669 C++
fbclient.dll!fb_shutdown(timeout=0, reason=-3) Line 3791 C++
isql.exe!atexit_fb_shutdown() Line 1811 C++
[External Code]
|
|
|
|
Переход к форуму:
Текущее время: Fri Nov 22 02:17:18 GMT+3 2024
Общее время, затраченное на создание страницы: 0.01087 секунд
|