include/systools/win32/wait_for_multiple_objects.hxx | 37 +++++++++++++++++++ sal/osl/w32/conditn.cxx | 32 ++++------------ vcl/win/dtrans/MtaOleClipb.cxx | 30 +-------------- 3 files changed, 49 insertions(+), 50 deletions(-)
New commits: commit d86d2dc58716d3605f0d8b09623400c57c3d9809 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Fri Jan 10 16:19:10 2025 +0500 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Mon Feb 17 16:34:18 2025 +0500 Factor out and simplify COM-safe wait Before commit 1e2e51607a163021ef1fb1fb0d217266bd448173 (Try to use CoWaitForMultipleHandles magic to handle COM message loop, 2025-01-10) osl_waitCondition didn't adjust the timeout value in the COM message processing loop. The abovementioned commit started to use a function that pumps the message loop itself, but the code for manual pumping wasn't removed (it has (likely) became dead). This drops the dead code, and deduplicates the rest. Change-Id: I0f7bbf8220792fbe42178c2689a065b5f38645d6 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/180069 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> diff --git a/include/systools/win32/wait_for_multiple_objects.hxx b/include/systools/win32/wait_for_multiple_objects.hxx new file mode 100644 index 000000000000..bcffe7013d8b --- /dev/null +++ b/include/systools/win32/wait_for_multiple_objects.hxx @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <sal/config.h> + +#include <prewin.h> +#include <objbase.h> +#include <postwin.h> + +namespace sal::systools +{ +/* Like WaitForMultipleObjects function, but makes sure to process COM messages during the wait, + * allowing other threads access to COM objects instantiated in this thread. + */ +DWORD WaitForMultipleObjects_COMDispatch(DWORD count, const HANDLE* handles, DWORD timeout) +{ + DWORD dwResult; + HRESULT hr = CoWaitForMultipleHandles(COWAIT_DISPATCH_CALLS, timeout, count, + const_cast<LPHANDLE>(handles), &dwResult); + if (hr == RPC_S_CALLPENDING) + return WAIT_TIMEOUT; + if (FAILED(hr)) + return WAIT_FAILED; + + return dwResult; +} +} // sal::systools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sal/osl/w32/conditn.cxx b/sal/osl/w32/conditn.cxx index 0ad233ed9bff..9063899533c3 100644 --- a/sal/osl/w32/conditn.cxx +++ b/sal/osl/w32/conditn.cxx @@ -22,6 +22,7 @@ #include <osl/conditn.h> #include <osl/diagnose.h> #include <osl/time.h> +#include <systools/win32/wait_for_multiple_objects.hxx> /* under WIN32, we use the void* oslCondition @@ -75,39 +76,17 @@ oslConditionResult SAL_CALL osl_waitCondition(oslCondition Condition, /* It's necessary to process SendMessage calls to the current thread to give other threads access to COM objects instantiated in this thread */ - - while ( true ) + switch (sal::systools::WaitForMultipleObjects_COMDispatch( + 1, reinterpret_cast<HANDLE*>(&Condition), timeout)) { - DWORD index; - /* Only wake up if a SendMessage / COM call to the threads message loop is detected */ - HRESULT hr = CoWaitForMultipleHandles(COWAIT_DISPATCH_CALLS, timeout, 1, - reinterpret_cast<HANDLE*>(&Condition), &index); - if (hr == RPC_S_CALLPENDING) - return osl_cond_result_timeout; - if (FAILED(hr)) - return osl_cond_result_error; - switch (index) - { - case WAIT_OBJECT_0 + 1: - { - MSG msg; - - /* We Must not dispatch the message. PM_NOREMOVE leaves the message queue untouched - but dispatches SendMessage calls automatically */ + case WAIT_OBJECT_0: + return osl_cond_result_ok; - PeekMessageW( &msg, nullptr, 0, 0, PM_NOREMOVE ); - } - break; - - case WAIT_OBJECT_0: - return osl_cond_result_ok; - - case WAIT_TIMEOUT: - return osl_cond_result_timeout; + case WAIT_TIMEOUT: + return osl_cond_result_timeout; - default: - return osl_cond_result_error; - } + default: + return osl_cond_result_error; } } diff --git a/vcl/win/dtrans/MtaOleClipb.cxx b/vcl/win/dtrans/MtaOleClipb.cxx index 494c89adc822..994587b21f64 100644 --- a/vcl/win/dtrans/MtaOleClipb.cxx +++ b/vcl/win/dtrans/MtaOleClipb.cxx @@ -45,6 +45,7 @@ #include <systools/win32/comtools.hxx> #include <systools/win32/retry_if_failed.hxx> +#include <systools/win32/wait_for_multiple_objects.hxx> #include <comphelper/windowserrorstring.hxx> @@ -67,8 +68,6 @@ namespace /* private */ const bool INIT_NONSIGNALED = false; /* Similar to osl conditions, with two condition objects passed to the Wait function. - COM Proxy-Stub communication uses SendMessages for - synchronization purposes. */ class Win32Condition { @@ -81,37 +80,15 @@ namespace /* private */ // leave messages sent through bool wait(HANDLE hEvtAbort) { - HANDLE hWaitArray[2] = { m_hEvent, hEvtAbort }; - while (true) + const HANDLE hWaitArray[2] = { m_hEvent, hEvtAbort }; + switch (sal::systools::WaitForMultipleObjects_COMDispatch(2, hWaitArray, INFINITE)) { - DWORD dwResult; - HRESULT hr = CoWaitForMultipleHandles(COWAIT_DISPATCH_CALLS, INFINITE, 2, - hWaitArray, &dwResult); - if (FAILED(hr)) - return false; - - switch (dwResult) - { case WAIT_OBJECT_0: // wait successful return true; case WAIT_OBJECT_0 + 1: // wait aborted - return false; - - case WAIT_OBJECT_0 + 2: - { - /* PeekMessage processes all messages in the SendMessage - queue that's what we want, messages from the PostMessage - queue stay untouched */ - MSG msg; - PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE); - - break; - } - default: // WAIT_FAILED? return false; - } } } commit 9d8b4403e640f26df308a57739fee378f5a86121 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Fri Jan 10 11:52:51 2025 +0500 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Mon Feb 17 16:34:12 2025 +0500 Try to use CoWaitForMultipleHandles magic to handle COM message loop A customer reports a freeze problem, with the call stacks seemengly indicating a shuck COM re-entry, even though osl_waitCondition should be prepared for COM re-entry. The two relevant threads are: 1. VCL Main win32u.dll!NtUserMsgWaitForMultipleObjectsEx() user32.dll!RealMsgWaitForMultipleObjectsEx() combase.dll!CCliModalLoop::BlockFn(void * * ahEvent, unsigned long cEvents, unsigned long * lpdwSignaled) Line 2156 [Inlineframe] combase.dll!ModalLoop(CSyncClientCall *) Line 164 combase.dll!ClassicSTAThreadWaitForCall(CSyncClientCall * pClientCall, WaitForCallReason reason, unsigned long dwRetryTimeout) Line 172 [Inlineframe] combase.dll!ThreadSendReceive(tagRPCOLEMESSAGE *) Line 7355 [Inlineframe] combase.dll!CSyncClientCall::SwitchAptAndDispatchCall(tagRPCOLEMESSAGE * pMessage) Line 5900 combase.dll!CSyncClientCall::SendReceive2(tagRPCOLEMESSAGE * pMessage, unsigned long * pstatus) Line 5459 [Inlineframe] combase.dll!SyncClientCallRetryContext::SendReceiveWithRetry(tagRPCOLEMESSAGE *) Line 1542 [Inlineframe] combase.dll!CSyncClientCall::SendReceiveInRetryContext(SyncClientCallRetryContext *) Line 565 combase.dll!ClassicSTAThreadSendReceive(CSyncClientCall * pClientCall, tagRPCOLEMESSAGE * pMsg, unsigned long * pulStatus) Line 547 combase.dll!CSyncClientCall::SendReceive(tagRPCOLEMESSAGE * pMessage, unsigned long * pulStatus) Line 783 combase.dll!CClientChannel::SendReceive(tagRPCOLEMESSAGE * pMessage, unsigned long * pulStatus) Line 655 combase.dll!NdrExtpProxySendReceive(void * pThis, _MIDL_STUB_MESSAGE * pStubMsg) Line 2002 rpcrt4.dll!NdrpClientCall3() rpcrt4.dll!NdrClientCall3() [Inlineframe] combase.dll!IDataObject_RemoteGetData_Proxy(IDataObject *) Line 2082 combase.dll!IDataObject_GetData_Proxy(IDataObject * This, tagFORMATETC * pformatetcIn, tagSTGMEDIUM * pMedium) Line 1267 ole32.dll!CDefObject::GetData(tagFORMATETC * pformatetcIn, tagSTGMEDIUM * pmedium) Line 1316 [Inlineframe] emboleobj.dll!rtl::OUString::indexOf(const char[13] &) Line 2030 [Inlineframe] emboleobj.dll!GetAspectFromFlavor(const com::sun::star::datatransfer::DataFlavor &) Line 215 emboleobj.dll!OleComponent::getTransferData(const com::sun::star::datatransfer::DataFlavor & aFlavor) Line 1556 [Inlineframe] emboleobj.dll!com::sun::star::uno::Type::{ctor}() Line 46 [Inlineframe] emboleobj.dll!com::sun::star::datatransfer::DataFlavor::{ctor}() Line 20 [Inlineframe] emboleobj.dll!com::sun::star::embed::VisualRepresentation::{ctor}() Line 19 emboleobj.dll!OleEmbeddedObject::getPreferredVisualRepresentation(__int64 nAspect) Line 372 comphelper.dll!comphelper::EmbeddedObjectContainer::GetGraphicReplacementStream(__int64 nViewAspect, const com::sun::star::uno::Reference<com::sun::star::embed::XEmbeddedObject> & xObj, rtl::OUString * pMediaType) Line 1430 [Inlineframe] svtlo.dll!svt::EmbeddedObjectRef::GetGraphicReplacementStream(__int64) Line 916 svtlo.dll!svt::EmbeddedObjectRef::GetGraphicStream(bool bUpdate) Line 690 svtlo.dll!svt::EmbeddedObjectRef::GetReplacement(bool bUpdate) Line 486 svtlo.dll!svt::EmbeddedObjectRef::GetGraphic() Line 524 swlo.dll!OutHTML_FrameFormatOLENodeGrf(SwHTMLWriter & rWrt, const SwFrameFormat & rFrameFormat, bool bInCntnr, bool bWriteReplacementGraphic) Line 1652 swlo.dll!SwHTMLWriter::OutFrameFormat(AllHtmlFlags nMode, const SwFrameFormat & rFrameFormat, const SdrObject * pSdrObject) Line 464 swlo.dll!OutHTML_SwFlyCnt(SwHTMLWriter & rWrt, const SfxPoolItem & rHt) Line 2974 swlo.dll!Out(SwHTMLWriter &(*)(SwHTMLWriter &, const SfxPoolItem &) * pTab, const SfxPoolItem & rHt, SwHTMLWriter & rWrt) Line 40 swlo.dll!OutHTML_SwTextNode(SwHTMLWriter & rWrt, const SwContentNode & rNode) Line 2477 swlo.dll!SwHTMLWriter::Out_SwDoc(SwPaM * pPam) Line 960 swlo.dll!SwHTMLWriter::WriteStream() Line 612 swlo.dll!Writer::Write(SwPaM & rPaM, SvStream & rStrm, const rtl::OUString * pFName) Line 236 swlo.dll!Writer::Write(SwPaM & rPam, SfxMedium & rMedium, const rtl::OUString * pFileName) Line 250 swlo.dll!SwWriter::Write(const tools::SvRef<Writer> & rxWriter, const rtl::OUString * pRealFileName) Line 872 swlo.dll!SwDocShell::ConvertTo(SfxMedium & rMedium) Line 778 sfxlo.dll!SfxObjectShell::SaveTo_Impl(SfxMedium & rMedium, const SfxItemSet * pSet) Line 1633 sfxlo.dll!SfxObjectShell::PreDoSaveAs_Impl(const rtl::OUString & rFileName, const rtl::OUString & aFilterName, const SfxItemSet & rItemSet, const com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> & rArgs) Line 3120 sfxlo.dll!SfxObjectShell::CommonSaveAs_Impl(const INetURLObject & aURL, const rtl::OUString & aFilterName, SfxItemSet & rItemSet, const com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> & rArgs) Line 2910 sfxlo.dll!SfxObjectShell::APISaveAs_Impl(std::basic_string_view<char16_t,std::char_traits<char16_t>> aFileName, SfxItemSet & rItemSet, const com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> & rArgs) Line 320 sfxlo.dll!SfxBaseModel::impl_store(const rtl::OUString & sURL, const com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> & seqArguments, bool bSaveTo) Line 3203 [Inlineframe] sfxlo.dll!SfxBaseModel::storeToURL::__l7::<lambda_1>::operator()() Line 1813 sfxlo.dll!vcl::solarthread::detail::GenericSolarThreadExecutor<`SfxBaseModel::storeToURL'::`7'::<lambda_1>,void>::doIt() Line 114 [Inlineframe] vcllo.dll!vcl::SolarThreadExecutor::worker(void *) Line 37 vcllo.dll!vcl::SolarThreadExecutor::LinkStubworker(void * instance, void * data) Line 32 [Inlineframe] vcllo.dll!Link<void *,void>::Call(void *) Line 111 [Inlineframe] vcllo.dll!ImplHandleUserEvent(ImplSVEvent *) Line 2287 vcllo.dll!ImplWindowFrameProc(vcl::Window * _pWindow, SalEvent nEvent, const void * pEvent) Line 2851 vcllo.dll!SalFrame::CallCallback(SalEvent nEvent, const void * pEvent) Line 310 [Inlineframe] vclplug_winlo.dll!ImplHandleAppCommand(HWND__ *) Line 5496 vclplug_winlo.dll!SalFrameWndProc(HWND__ * hWnd, unsigned int nMsg, unsigned __int64 wParam, __int64 lParam, bool & rDef) Line 6159 vclplug_winlo.dll!SalFrameWndProc(HWND__ * hWnd, unsigned int nMsg, unsigned __int64 wParam, __int64 lParam, bool & rDef) Line 6190 user32.dll!UserCallWinProcCheckWow(struct _ACTIVATION_CONTEXT *,__int64 (*)(struct tagWND *,unsigned int,unsigned __int64,__int64),struct HWND__ *,enum _WM_VALUE,unsigned __int64,__int64,void *,int) user32.dll!DispatchMessageWorker() vclplug_winlo.dll!ImplSalDispatchMessage(const tagMSG * pMsg) Line 476 vclplug_winlo.dll!ImplSalYield(bool bWait, bool bHandleAllCurrentEvents) Line 544 vclplug_winlo.dll!WinSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents) Line 581 vcllo.dll!ImplYield(bool i_bWait, bool i_bAllEvents) Line 393 vcllo.dll!Application::Execute() Line 369 sofficeapp.dll!desktop::Desktop::Main() Line 1605 vcllo.dll!ImplSVMain() Line 229 sofficeapp.dll!soffice_main() Line 94 vText.dll!00007ff7f1bb105b() vText.dll!00007ff7f1bb1304() kernel32.dll!00007fff114b7374() ntdll.dll!00007fff128fcc91() 2. cppu_threadpool::ORequestThread win32u.dll!NtUserMsgWaitForMultipleObjectsEx() user32.dll!RealMsgWaitForMultipleObjectsEx() sal3.dll!osl_waitCondition(void * Condition, const TimeValue * pTimeout) Line 82 [Inlineframe] vcllo.dll!osl::Condition::wait(const TimeValue *) Line 123 vcllo.dll!vcl::SolarThreadExecutor::execute() Line 63 sfxlo.dll!vcl::solarthread::detail::GenericSolarThreadExecutor<`SfxBaseModel::storeToURL'::`7'::<lambda_1>,void>::exec(const SfxBaseModel::storeToURL::__l7::<lambda_1> & func) Line 103 [Inlineframe] sfxlo.dll!vcl::solarthread::syncExecute(const SfxBaseModel::storeToURL::__l7::<lambda_1> &) Line 167 sfxlo.dll!SfxBaseModel::storeToURL(const rtl::OUString & rURL, const com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> & rArgs) Line 1813 mscx_uno.dll!`anonymous namespace'::cpp_call(bridges::cpp_uno::shared::UnoInterfaceProxy * pThis, bridges::cpp_uno::shared::VtableSlot aVtableSlot, _typelib_TypeDescriptionReference * pReturnTypeRef, long nParams, _typelib_MethodParameter * pParams, void * pUnoReturn, void * * pUnoArgs, _uno_Any * * ppUnoExc) Line 214 mscx_uno.dll!unoInterfaceProxyDispatch(_uno_Interface * pUnoI, const _typelib_TypeDescription * pMemberTD, void * pReturn, void * * pArgs, _uno_Any * * ppException) Line 430 binaryurplo.dll!binaryurp::IncomingRequest::execute_throw(binaryurp::BinaryAny * returnValue, std::vector<binaryurp::BinaryAny,std::allocator<binaryurp::BinaryAny>> * outArguments) Line 239 binaryurplo.dll!binaryurp::IncomingRequest::execute() Line 79 binaryurplo.dll!request(void * pThreadSpecificData) Line 84 cppu3.dll!cppu_threadpool::JobQueue::enter(const void * nDisposeId, bool bReturnWhenNoJob) Line 101 cppu3.dll!cppu_threadpool::ORequestThread::run() Line 169 cppu3.dll!threadFunc(void * param) Line 190 sal3.dll!oslWorkerWrapperFunction(void * pData) Line 67 ucrtbase.dll!00007fff106f1bb2() kernel32.dll!00007fff114b7374() ntdll.dll!00007fff128fcc91() In fact, this is not even obvious that the problem is really re-entry; it could also turn out to be a call to an external COM server. I can't debug the problem myself, so this is just a guess. A nice summary of the problem is in https://stackoverflow.com/questions/34420920; my suspicion is that our MsgWaitForMultipleObjects call doesn't include some message types needed for proper COM message pumping, sufficiently defined as "a small set of special-cased messages" in COWAIT_FLAGS enumeration docs [1]. Let me try if CoWaitForMultipleHandles in place of MsgWaitForMultipleObjects maybe resolves the issue. [1] https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/ne-combaseapi-cowait_flags Change-Id: I4eaac32cf08c7379a3f8b88bcdd851e70e97d757 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/180048 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> diff --git a/sal/osl/w32/conditn.cxx b/sal/osl/w32/conditn.cxx index 6bc6d9d3ed3a..0ad233ed9bff 100644 --- a/sal/osl/w32/conditn.cxx +++ b/sal/osl/w32/conditn.cxx @@ -78,8 +78,15 @@ oslConditionResult SAL_CALL osl_waitCondition(oslCondition Condition, while ( true ) { - /* Only wake up if a SendMessage call to the threads message loop is detected */ - switch( MsgWaitForMultipleObjects( 1, reinterpret_cast<HANDLE *>(&Condition), FALSE, timeout, QS_SENDMESSAGE ) ) + DWORD index; + /* Only wake up if a SendMessage / COM call to the threads message loop is detected */ + HRESULT hr = CoWaitForMultipleHandles(COWAIT_DISPATCH_CALLS, timeout, 1, + reinterpret_cast<HANDLE*>(&Condition), &index); + if (hr == RPC_S_CALLPENDING) + return osl_cond_result_timeout; + if (FAILED(hr)) + return osl_cond_result_error; + switch (index) { case WAIT_OBJECT_0 + 1: { diff --git a/vcl/win/dtrans/MtaOleClipb.cxx b/vcl/win/dtrans/MtaOleClipb.cxx index a62f4a1c0c72..494c89adc822 100644 --- a/vcl/win/dtrans/MtaOleClipb.cxx +++ b/vcl/win/dtrans/MtaOleClipb.cxx @@ -66,11 +66,7 @@ namespace /* private */ const bool MANUAL_RESET = true; const bool INIT_NONSIGNALED = false; - /* Cannot use osl conditions because they are blocking - without waking up on messages sent by another thread - this leads to deadlocks because we are blocking the - communication between inter-thread marshalled COM - pointers. + /* Similar to osl conditions, with two condition objects passed to the Wait function. COM Proxy-Stub communication uses SendMessages for synchronization purposes. */ @@ -85,11 +81,14 @@ namespace /* private */ // leave messages sent through bool wait(HANDLE hEvtAbort) { - const HANDLE hWaitArray[2] = { m_hEvent, hEvtAbort }; + HANDLE hWaitArray[2] = { m_hEvent, hEvtAbort }; while (true) { - DWORD dwResult - = MsgWaitForMultipleObjects(2, hWaitArray, FALSE, INFINITE, QS_SENDMESSAGE); + DWORD dwResult; + HRESULT hr = CoWaitForMultipleHandles(COWAIT_DISPATCH_CALLS, INFINITE, 2, + hWaitArray, &dwResult); + if (FAILED(hr)) + return false; switch (dwResult) {