Есть out-of-proc COM сервер с COM синглетоном Engine, реализованным с помощью стандартного DECLARE_CLASSFACTORY_SINGLETON. Работает в STA (CComSingleThreadModel, _ATL_APARTMENT_THREADED).
Клиенты COM сервера:
1. ActiveScript (JScript), в который с помощью AddNamedItem передается ссылка на Engine.
2. Несколько независимых IE BHO, работающих в STA.
BHO периодически вызывают у Engine метод DispatchEvent (по событиям браузера передавая свой объект), Engine в свою очередь сразу же вызывает JavaScript функцию (или несколько) внутри ActiveScript.
Эта схема отлично работает до тех пор, пока не включишь сразу два BHO (если включить один любой из BHO то все ок).
Если включить два BHO, на вызове функции в ActiveScript (с помощью IDispatch/Invoke) в случайные моменты происходит зависание.
Дополнительные потоки мною не создаются.
Некоторые наблюдения:
— Если в ActiveScript не передавать объект пришедший из BHO, а подменить его на такой же, но созданный внутри Engine, зависания нет.
— Зависание происходит только при срабатывании сборщика мусора в ActiveScript при попытке сделать Release объекту, переданному из BHO (IUnknown_Release_Proxy в коллстеке).
Колстек зависания:
> ntdll.dll!_ZwWaitForMultipleObjects@20() + 0x15 bytes
ntdll.dll!_ZwWaitForMultipleObjects@20() + 0x15 bytes
KernelBase.dll!_WaitForMultipleObjectsEx@20() + 0x100 bytes
kernel32.dll!_WaitForMultipleObjectsExImplementation@20() + 0x8e bytes
user32.dll!_RealMsgWaitForMultipleObjectsEx@20() + 0xe2 bytes
ole32.dll!CCliModalLoop::BlockFn(void * * ahEvent, unsigned long cEvents, unsigned long * lpdwSignaled) Line 1222 C++
ole32.dll!ModalLoop(CMessageCall * pcall) Line 211 C++
ole32.dll!ThreadSendReceive(CMessageCall * pCall) Line 4979 C++
ole32.dll!CRpcChannelBuffer::SwitchAptAndDispatchCall(CMessageCall * * ppCall) Line 4454 + 0x6 bytes C++
ole32.dll!CRpcChannelBuffer::SendReceive2(tagRPCOLEMESSAGE * pMessage, unsigned long * pstatus) Line 4076 C++
ole32.dll!CCliModalLoop::SendReceive(tagRPCOLEMESSAGE * pMsg, unsigned long * pulStatus, IInternalChannelBuffer * pChnl) Line 899 + 0x17 bytes C++
ole32.dll!CAptRpcChnl::SendReceive(tagRPCOLEMESSAGE * pMsg, unsigned long * pulStatus) Line 583 + 0xd bytes C++
ole32.dll!CCtxComChnl::SendReceive(tagRPCOLEMESSAGE * pMessage, unsigned long * pulStatus) Line 734 + 0xa bytes C++
ole32.dll!NdrExtpProxySendReceive(void * pThis, _MIDL_STUB_MESSAGE * pStubMsg) Line 1932 C++
rpcrt4.dll!@NdrpProxySendReceive@4() + 0xe bytes
rpcrt4.dll!_NdrClientCall2() + 0x144 bytes
ole32.dll!ObjectStublessClient(void * ParamAddress, long Method) Line 474 + 0x8 bytes C++
ole32.dll!_ObjectStubless@0() Line 154 Asm
ole32.dll!RemoteReleaseRifRefHelper(IRemUnknown * pRemUnk, int fReleaseRemUnkProxy, int fProcessingPostedMessage, OXIDEntry * pOXIDEntry, unsigned short cRifRef, tagREMINTERFACEREF * pRifRef, IUnknown * pAsyncRelease) Line 6770 + 0xc bytes C++
ole32.dll!RemoteReleaseRifRef(CStdMarshal * pMarshal, OXIDEntry * pOXIDEntry, unsigned short cRifRef, tagREMINTERFACEREF * pRifRef) Line 6694 C++
ole32.dll!CStdMarshal::DisconnectCliIPIDs() Line 3964 C++
ole32.dll!CStdMarshal::Disconnect(unsigned long dwType) Line 3273 C++
ole32.dll!CStdIdentity::~CStdIdentity() Line 312 C++
ole32.dll!CStdIdentity::`scalar deleting destructor'() + 0xd bytes C++
ole32.dll!CStdIdentity::CInternalUnk::Release() Line 767 C++
ole32.dll!IUnknown_Release_Proxy(IUnknown * This) Line 1773 C++
oleaut32.dll!_VariantClear@4() + 0xac9 bytes
jscript.dll!VAR::Clear() + 0x50 bytes
jscript.dll!GcAlloc::ReclaimGarbage() + 0xa2 bytes
jscript.dll!GcContext::Reclaim() + 0x8e bytes
jscript.dll!GcContext::CollectCore() - 0x72f bytes
jscript.dll!GcContext::Collect() + 0x34 bytes
jscript.dll!CScriptRuntime::Run() - 0x864f bytes
jscript.dll!ScrFncObj::CallWithFrameOnStack() + 0xf3 bytes
jscript.dll!ScrFncObj::Call() + 0x84 bytes
jscript.dll!NameTbl::InvokeInternal() + 0x113 bytes
jscript.dll!VAR::InvokeByDispID() + 0x73 bytes
jscript.dll!CScriptRuntime::Run() + 0x1d89 bytes
jscript.dll!ScrFncObj::CallWithFrameOnStack() + 0xf3 bytes
jscript.dll!ScrFncObj::Call() + 0x84 bytes
jscript.dll!NameTbl::InvokeInternal() + 0x113 bytes
jscript.dll!VAR::InvokeByDispID() + 0x73 bytes
jscript.dll!CScriptRuntime::Run() + 0x1d89 bytes
jscript.dll!ScrFncObj::CallWithFrameOnStack() + 0xf3 bytes
jscript.dll!ScrFncObj::Call() + 0x84 bytes
jscript.dll!NameTbl::InvokeInternal() + 0x12c6 bytes
jscript.dll!VAR::InvokeByDispID() + 0x73 bytes
jscript.dll!NameTbl::GetVal() + 0x3b bytes
Здравствуйте, KAdot, Вы писали:
KA> Engine в свою очередь сразу же вызывает JavaScript функцию (или несколько) внутри ActiveScript.
Инстанс ActiveScript создается на каждый вызов Engine из BHO, или он общий на всех?
Если общий, то возможно проблема в STA-модели WSH, нужно делать маршаллинг и крутить циклы сообщений...
Здравствуйте, rus blood, Вы писали:
RB>Инстанс ActiveScript создается на каждый вызов Engine из BHO, или он общий на всех?
ActiveScript создается один раз и он общий для всех клиентов.
RB>Если общий, то возможно проблема в STA-модели WSH, нужно делать маршаллинг и крутить циклы сообщений...
Маршаллинг между ActiveScript и Engine? В какое место добавить цикл сообщений? На сколько я понимаю, он уже и так есть в ATL::CAtlExeModuleT.
Здравствуйте, KAdot, Вы писали:
KA>ActiveScript создается один раз и он общий для всех клиентов. KA>Маршаллинг между ActiveScript и Engine? В какое место добавить цикл сообщений? На сколько я понимаю, он уже и так есть в ATL::CAtlExeModuleT.
Есть out-of-proc COM сервер с COM синглетоном Engine, реализованным с помощью стандартного DECLARE_CLASSFACTORY_SINGLETON. Работает в STA (CComSingleThreadModel, _ATL_APARTMENT_THREADED).
Ну CComSingleThreadModel, _ATL_APARTMENT_THREADED — это опции построения компоненты.
Реальный апартмент будет определяться настройками и конфигурированием.
Кто хостит COM-объект — COM+, exe-сервер или сервис?
Синглетон действительно в одном потоке живет, проверял?
Здравствуйте, rus blood, Вы писали:
RB>Синглетон действительно в одном потоке живет, проверял?
Да, выводил при вызове методов синглтона лог с Thread ID — всегда был одинаков.
Разобрался более подробно как именно происходит дедлок:
1. Клиент вызывает Engine::dispatchEvent (через Invoke), и пытается дождаться ответа сервера (в коллстеке oleaut32.dll!_IDispatch_Invoke_Proxy, user32.dll!_RealMsgWaitForMultipleObjectsEx).
2. До сервера доходит запроса и он передает объект в JScript, в нем срабатывает сборщик мусора и вызывается Release у старых объектов, переданных ранее из клиента (IUnknown_Release_Proxy, MsgWaitForMultipleObjects).
3. В итоге имеем, что клиент ждет ответа сервера, в этот момент сервер начинает ждать ответа клиента и все зависает.
4. На сколько мне известно COM должен корректно отрабатывать такие ситуации, используя MsgWaitForMultipleObjects для прокачки сообщения во время ожидания, но тем не менее дедлок все равно происходит.
Здравствуйте, KAdot, Вы писали:
KA>1. Клиент вызывает Engine::dispatchEvent (через Invoke), и пытается дождаться ответа сервера (в коллстеке oleaut32.dll!_IDispatch_Invoke_Proxy, user32.dll!_RealMsgWaitForMultipleObjectsEx). KA>2. До сервера доходит запроса и он передает объект в JScript, в нем срабатывает сборщик мусора и вызывается Release у старых объектов, переданных ранее из клиента (IUnknown_Release_Proxy, MsgWaitForMultipleObjects). KA>3. В итоге имеем, что клиент ждет ответа сервера, в этот момент сервер начинает ждать ответа клиента и все зависает. KA>4. На сколько мне известно COM должен корректно отрабатывать такие ситуации, используя MsgWaitForMultipleObjects для прокачки сообщения во время ожидания, но тем не менее дедлок все равно происходит.
Т.е., ситуация такая.
Клиент создает инстанс синглетона и еще какие-то объекты, которые сам хостит.
Клиент вызывает метод синглетона в который передает эти объекты.
Синглетон вызывает скриптинг, из которого дергаются какие-то методы переданных объектов.
Так?
Тогда ситуация пункта 3 у тебя должна возникать постоянно...
А можешь проверить, в каких потоках "живут" объекты на клиентской стороне?
По поводу MsgWaitForMultipleObjects можно через IMessageFilter посмотреть, есть входящие обращения к клиенту во время вызова метода.
Также, полагаю, выгружать скриптинг после завершения метода сниглетона для тебя не выход...
Здравствуйте, rus blood, Вы писали:
RB>Т.е., ситуация такая. RB>Клиент создает инстанс синглетона и еще какие-то объекты, которые сам хостит. RB>Клиент вызывает метод синглетона в который передает эти объекты. RB>Синглетон вызывает скриптинг, из которого дергаются какие-то методы переданных объектов.
Да.
RB>А можешь проверить, в каких потоках "живут" объекты на клиентской стороне?
На клиенте 1 всегда свой поток №1. CoGetApartmentType возвращает APTTYPE = APTTYPE_STA.
На клиенте 2 всегда свой поток №2. CoGetApartmentType возвращает APTTYPE = APTTYPE_STA.
На сервере всегда свой поток №3. CoGetApartmentType возвращает APTTYPE = APTTYPE_MAINSTA.
RB>По поводу MsgWaitForMultipleObjects можно через IMessageFilter посмотреть, есть входящие обращения к клиенту во время вызова метода.
Спасибо, попробую.
RB>Также, полагаю, выгружать скриптинг после завершения метода сниглетона для тебя не выход...
В ActiveScript находится логика по обработке событий от клиентов и выгружать его нельзя.
Здравствуйте, KAdot, Вы писали:
KA>На клиенте 2 всегда свой поток №2. CoGetApartmentType возвращает APTTYPE = APTTYPE_STA.
Подебажил еще, из-за особенностей реализации все-же в клиенте №2 могут быть разные потоки.
Здравствуйте, KAdot, Вы писали:
KA>Подебажил еще, из-за особенностей реализации все-же в клиенте №2 могут быть разные потоки.
Попробовал в клиенте 2 переместить все в один поток — не помогло.