vcl/headless/svpinst.cxx | 7 +++++++ vcl/inc/headless/svpinst.hxx | 1 + vcl/inc/salinst.hxx | 4 ++++ vcl/osx/salinst.cxx | 22 +--------------------- vcl/quartz/cgutils.mm | 26 ++++++++++++++++++++++++++ 5 files changed, 39 insertions(+), 21 deletions(-)
New commits: commit c9551f82891cffbc55e85ea87ff6dac5b1ed114a Author: Caolán McNamara <[email protected]> AuthorDate: Tue Oct 7 21:14:54 2025 +0100 Commit: Miklos Vajna <[email protected]> CommitDate: Wed Oct 8 13:09:11 2025 +0200 Pre-init NSSpellCHecker to avoid SolarMutex deadlock in headless vclplug too. Same deadlock can happen in that mode as seen originally as: commit d193d65635de197efe32d97a99540a31a5455c41 Date: Thu Sep 8 17:30:38 2022 +0200 tdf#151894 Pre-init NSSpellCHecker to avoid SolarMutex deadlock current example bts are main on thread 1, vcl launched to run on its event loop on 'lo_startmain' side thread. (lldb) thread backtrace 1 * thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP * frame #0: 0x00000001966f489c libsystem_kernel.dylib`__psynch_mutexwait + 8 frame #1: 0x0000000196730e58 libsystem_pthread.dylib`_pthread_mutex_firstfit_lock_wait + 84 frame #2: 0x000000019672e840 libsystem_pthread.dylib`_pthread_mutex_firstfit_lock_slow + 220 frame #3: 0x0000000105ad554c libuno_sal.dylib.3`osl_acquireMutex(pMutex=<unavailable>) at mutex.cxx:99:20 frame #4: 0x000000015a6e8370 libmergedlo.dylib`SvpSalYieldMutex::doAcquire(unsigned int) [inlined] osl::Mutex::acquire(this=0x00000001497e7ec8) at mutex.hxx:63:20 frame #5: 0x000000015a6e8368 libmergedlo.dylib`SvpSalYieldMutex::doAcquire(this=0x00000001497e7ec0, nLockCount=1) at svpinst.cxx:357:18 frame #6: 0x000000015952fda8 libmergedlo.dylib`doc_saveAs(_LibreOfficeKitDocument*, char const*, char const*, char const*) [inlined] comphelper::SolarMutex::acquire(this=0x00000001497e7ec0, nLockCount=1) at solarmutex.hxx:86:5 frame #7: 0x000000015952fd98 libmergedlo.dylib`doc_saveAs(_LibreOfficeKitDocument*, char const*, char const*, char const*) [inlined] osl::Guard<comphelper::SolarMutex>::Guard(this=<unavailable>, t=0x00000001497e7ec0) at mutex.hxx:144:17 frame #8: 0x000000015952fd98 libmergedlo.dylib`doc_saveAs(_LibreOfficeKitDocument*, char const*, char const*, char const*) [inlined] SolarMutexGuard::SolarMutexGuard(this=<unavailable>) at svapp.hxx:1344:11 frame #9: 0x000000015952fd90 libmergedlo.dylib`doc_saveAs(_LibreOfficeKitDocument*, char const*, char const*, char const*) [inlined] SolarMutexGuard::SolarMutexGuard(this=<unavailable>) at svapp.hxx:1344:78 frame #10: 0x000000015952fd90 libmergedlo.dylib`doc_saveAs(pThis=0x000060000124a9a0, sUrl="/tmp/ErrareHumanumEst.fodg", pFormat="fodg", pFilterOptions="{\"DecomposePDF\":{\"type\":\"boolean\",\"value\":\"true\"}}") at init.cxx:3698:21 ... frame #32: 0x0000000102965334 main + 228 (lldb) thread backtrace 11 thread #11, name = 'lo_startmain' frame #0: 0x00000001966f53cc libsystem_kernel.dylib`__psynch_cvwait + 8 frame #1: 0x00000001967340e0 libsystem_pthread.dylib`_pthread_cond_wait + 984 frame #2: 0x0000000197ddefd0 Foundation`-[NSOperation waitUntilFinished] + 488 frame #3: 0x00000001967e28f8 CoreFoundation`_CFXNotificationPost + 804 frame #4: 0x0000000197d9c680 Foundation`-[NSNotificationCenter postNotificationName:object:userInfo:] + 88 frame #5: 0x000000019a737554 AppKit`-[NSMenu insertItem:atIndex:] + 892 frame #6: 0x000000019a8f1e00 AppKit`-[NSApplication(NSServicesMenuPrivate) _fillSpellCheckerPopupButton:] + 1380 frame #7: 0x000000019a8f1550 AppKit`-[NSSpellChecker _fillSpellCheckerPopupButton:] + 76 frame #8: 0x000000019a8f0688 AppKit`-[NSSpellChecker init] + 248 frame #9: 0x000000019a8f0580 AppKit`__36+[NSSpellChecker sharedSpellChecker]_block_invoke + 20 frame #10: 0x000000019659585c libdispatch.dylib`_dispatch_client_callout + 16 frame #11: 0x000000019657ea28 libdispatch.dylib`_dispatch_once_callout + 32 frame #12: 0x000000019a8f0568 AppKit`+[NSSpellChecker sharedSpellChecker] + 140 frame #13: 0x0000000158dbb054 libmergedlo.dylib`MacSpellChecker::getLocales(this=0x00006000006fc500) at macspellimp.mm:118:42 frame #14: 0x0000000158da1e94 libmergedlo.dylib`LngSvcMgr::GetAvailableSpellSvcs_Impl(this=0x000000014966e020) at lngsvcmgr.cxx:975:63 frame #15: 0x0000000158da46f8 libmergedlo.dylib`LngSvcMgr::getAvailableServices(this=0x000000014966e020, rServiceName=0x000000015c044600, rLocale=0x000000016e3ae1d0) at lngsvcmgr.cxx:1369:9 frame #16: 0x0000000158d9c434 libmergedlo.dylib`LngSvcMgr::UpdateAll(this=0x000000014966e020) at lngsvcmgr.cxx:655:46 frame #17: 0x0000000158d9bd38 libmergedlo.dylib`LngSvcMgr::LngSvcMgr(this=0x000000014966e020) at lngsvcmgr.cxx:414:5 frame #18: 0x0000000158da7a70 libmergedlo.dylib`::linguistic_LngSvcMgr_get_implementation(com::sun::star::uno::XComponentContext *, const com::sun::star::uno::Sequence<com::sun::star::uno::Any> &) [inlined] LngSvcMgr::LngSvcMgr(this=0x000000014966e020) at lngsvcmgr.cxx:402:1 frame #19: 0x0000000158da7a6c libmergedlo.dylib`linguistic_LngSvcMgr_get_implementation((null)=<unavailable>, (null)=<unavailable>) at lngsvcmgr.cxx:1816:30 frame #20: 0x000000012dc30ee0 libuno_cppuhelpergcc3.dylib.3`cppuhelper::ServiceManager::Data::Implementation::doCreateInstance(com::sun::star::uno::Reference<com::sun::star::uno::XComponentContext> const&) [inlined] std::__1::__function::__value_func<com::sun::star::uno::XInterface* (com::sun::star::uno::XComponentContext*, com::sun::star::uno::Sequence<com::sun::star::uno::Any> const&)>::operator()[abi:ne190102](this=0x0000000149747c90, __args=0x000000016e3ae310, __args=0x000000016e3ae308) const at function.h:430:12 frame #21: 0x000000012dc30ec4 libuno_cppuhelpergcc3.dylib.3`cppuhelper::ServiceManager::Data::Implementation::doCreateInstance(com::sun::star::uno::Reference<com::sun::star::uno::XComponentContext> const&) [inlined] std::__1::function<com::sun::star::uno::XInterface* (com::sun::star::uno::XComponentContext*, com::sun::star::uno::Sequence<com::sun::star::uno::Any> const&)>::operator()(this= Function = linguistic_LngSvcMgr_get_implementation , __arg=0x0000600000cc86b8, __arg=0x000000016e3ae308) const at function.h:989:10 frame #22: 0x000000012dc30ec4 libuno_cppuhelpergcc3.dylib.3`cppuhelper::ServiceManager::Data::Implementation::doCreateInstance(this=0x0000000149747c18, context=0x000000015c487110) at servicemanager.cxx:701:13 frame #23: 0x000000012dc30d4c libuno_cppuhelpergcc3.dylib.3`cppuhelper::ServiceManager::Data::Implementation::createInstance(this=0x0000000149747c18, context=0x000000015c487110, singletonRequest=false) at servicemanager.cxx:666:30 frame #24: 0x000000012dc34ed8 libuno_cppuhelpergcc3.dylib.3`non-virtual thunk to cppuhelper::ServiceManager::createInstanceWithContext(rtl::OUString const&, com::sun::star::uno::Reference<com::sun::star::uno::XComponentContext> const&) [inlined] cppuhelper::ServiceManager::createInstanceWithContext(this=<unavailable>, aServiceSpecifier=<unavailable>, Context=0x000000015c487110) at servicemanager.cxx:1002:36 frame #25: 0x000000012dc34eb4 libuno_cppuhelpergcc3.dylib.3`non-virtual thunk to cppuhelper::ServiceManager::createInstanceWithContext(rtl::OUString const&, com::sun::star::uno::Reference<com::sun::star::uno::XComponentContext> const&) at servicemanager.cxx:0 frame #26: 0x00000001588b29c8 libmergedlo.dylib`com::sun::star::linguistic2::LinguServiceManager::create(the_context=0x000000015c487110) at LinguServiceManager.hpp:38:129 frame #27: 0x00000001588b3444 libmergedlo.dylib`(anonymous namespace)::SpellDummy_Impl::GetSpell_Impl() [inlined] GetLngSvcMgr_Impl() at unolingu.cxx:60:52 frame #28: 0x00000001588b3438 libmergedlo.dylib`(anonymous namespace)::SpellDummy_Impl::GetSpell_Impl(this=0x0000600002df4300) at unolingu.cxx:216:61 frame #29: 0x00000001588b3314 libmergedlo.dylib`non-virtual thunk to (anonymous namespace)::SpellDummy_Impl::isValid(rtl::OUString const&, short, com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> const&) [inlined] (anonymous namespace)::SpellDummy_Impl::isValid(this=<unavailable>, rWord=<unavailable>, nLanguage=<unavailable>, rProperties=<unavailable>) at unolingu.cxx:248:5 frame #30: 0x00000001588b3310 libmergedlo.dylib`non-virtual thunk to (anonymous namespace)::SpellDummy_Impl::isValid(rtl::OUString const&, short, com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> const&) at unolingu.cxx:0 frame #31: 0x00000001588405ec libmergedlo.dylib`ImpEditEngine::DoOnlineSpelling(this=0x0000000149859800, pThisNodeOnly=<unavailable>, bSpellAtCursorPos=<unavailable>, bInterruptible=<unavailable>) at impedit4.cxx:2480:37 frame #32: 0x0000000134059498 libsdlo.dylib`SdDrawDocument::SpellObject(this=0x000000014961cb20, pObj=0x0000000149646150) at drawdoc4.cxx:885:16 frame #33: 0x00000001340591b0 libsdlo.dylib`SdDrawDocument::OnlineSpellingHdl(this=0x000000014961cb20, (null)=<unavailable>) at drawdoc4.cxx:823:17 frame #34: 0x000000015a52b9b0 libmergedlo.dylib`Scheduler::CallbackTaskScheduling() at scheduler.cxx:584:20 frame #35: 0x000000015a6e7bf8 libmergedlo.dylib`SvpSalInstance::CheckTimeout(bool) [inlined] SalTimer::CallCallback(this=<unavailable>) at saltimer.hxx:53:13 frame #36: 0x000000015a6e7bec libmergedlo.dylib`SvpSalInstance::CheckTimeout(this=<unavailable>, bExecuteTimers=true) at svpinst.cxx:167:53 frame #37: 0x000000015a6e863c libmergedlo.dylib`SvpSalInstance::ImplYield(this=0x00006000008c50e0, bWait=true, bHandleAllCurrentEvents=false) at svpinst.cxx:430:17 frame #38: 0x000000015a6e8950 libmergedlo.dylib`SvpSalInstance::DoYield(this=0x00006000008c50e0, bWait=true, bHandleAllCurrentEvents=false) at svpinst.cxx:504:21 frame #39: 0x000000015a53d920 libmergedlo.dylib`ImplYield(i_bWait=true, i_bAllEvents=false) at svapp.cxx:389:48 frame #40: 0x000000015a53d328 libmergedlo.dylib`Application::Yield() at svapp.cxx:492:5 [artificial] frame #41: 0x000000015a53d210 libmergedlo.dylib`Application::Execute() at svapp.cxx:364:13 frame #42: 0x00000001594ffeb4 libmergedlo.dylib`desktop::Desktop::Main(this=0x000000016e3aeed0) at app.cxx:1680:13 frame #43: 0x000000015a5475d0 libmergedlo.dylib`ImplSVMain() at svmain.cxx:228:35 frame #44: 0xffffffffffffffff libmergedlo.dylib`SVMain() frame #45: 0x000000015951e5e4 libmergedlo.dylib`soffice_main at sofficemain.cxx:121:12 frame #46: 0x00000001595669bc libmergedlo.dylib`lo_startmain((null)=<unavailable>) at init.cxx:7860:5 frame #47: 0x0000000105ae59d8 libuno_sal.dylib.3`osl_thread_start_Impl(pData=0x00006000005cb330) at thread.cxx:240:9 frame #48: 0x0000000196733c0c libsystem_pthread.dylib`_pthread_start + 136 Change-Id: I8d25ed364f131434ce4f738604ddcf3f586cf6d9 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192052 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/vcl/headless/svpinst.cxx b/vcl/headless/svpinst.cxx index 267c5335a896..4220f5224ff6 100644 --- a/vcl/headless/svpinst.cxx +++ b/vcl/headless/svpinst.cxx @@ -106,6 +106,13 @@ SvpSalInstance::SvpSalInstance( std::unique_ptr<SalYieldMutex> pMutex ) #endif } +void SvpSalInstance::AfterAppInit() +{ +#if defined(MACOSX) + SalInstance::MacStartupWorkarounds(); +#endif +} + SvpSalInstance::~SvpSalInstance() { if( s_pDefaultInstance == this ) diff --git a/vcl/inc/headless/svpinst.hxx b/vcl/inc/headless/svpinst.hxx index f3d9205a8981..e600259829b9 100644 --- a/vcl/inc/headless/svpinst.hxx +++ b/vcl/inc/headless/svpinst.hxx @@ -110,6 +110,7 @@ public: static SvpSalInstance* s_pDefaultInstance; SvpSalInstance( std::unique_ptr<SalYieldMutex> pMutex ); + virtual void AfterAppInit() override; virtual ~SvpSalInstance() override; SAL_DLLPRIVATE bool ImplYield(bool bWait, bool bHandleAllCurrentEvents); diff --git a/vcl/inc/salinst.hxx b/vcl/inc/salinst.hxx index b8549b23fc5f..f3af5cf3794d 100644 --- a/vcl/inc/salinst.hxx +++ b/vcl/inc/salinst.hxx @@ -83,6 +83,10 @@ protected: bool m_bSupportsBitmap32 = false; bool m_bSupportsOpenGL = false; +#ifdef MACOSX + static void MacStartupWorkarounds(); +#endif + public: SalInstance(std::unique_ptr<comphelper::SolarMutex> pMutex); virtual ~SalInstance(); diff --git a/vcl/osx/salinst.cxx b/vcl/osx/salinst.cxx index 6d6cb6d49989..85202f7cf765 100644 --- a/vcl/osx/salinst.cxx +++ b/vcl/osx/salinst.cxx @@ -212,27 +212,7 @@ void AquaSalInstance::AfterAppInit() object: nil ]; #endif - // HACK: When the first call to [NSSpellChecker sharedSpellChecker] (in - // lingucomponent/source/spellcheck/macosxspell/macspellimp.mm) is done both on a thread other - // than the main thread and with the SolarMutex erroneously locked, then that can lead to - // deadlock as [NSSpellChecker sharedSpellChecker] internally calls - // AppKit`-[NSSpellChecker init] -> - // AppKit`-[NSSpellChecker _fillSpellCheckerPopupButton:] -> - // AppKit`-[NSApplication(NSServicesMenuPrivate) _fillSpellCheckerPopupButton:] -> - // AppKit`-[NSMenu insertItem:atIndex:] -> - // Foundation`-[NSNotificationCenter postNotificationName:object:userInfo:] -> - // CoreFoundation`_CFXNotificationPost -> - // Foundation`-[NSOperation waitUntilFinished] - // waiting for work to be done on the main thread, but the main thread is typically already - // blocked (in some event handling loop) waiting to acquire the SolarMutex. The real solution - // would be to fix all the cases where a call to [NSSpellChecker sharedSpellChecker] in - // lingucomponent/source/spellcheck/macosxspell/macspellimp.mm is done while the SolarMutex is - // locked (somewhere up the call chain), but that appears to be rather difficult (see e.g. - // <https://bugs.documentfoundation.org/show_bug.cgi?id=151894> "FILEOPEN a Base Document with - // customized event for open a startform by 'open document' LO stuck"). So, at least for now, - // chicken out and do that first call to [NSSpellChecker sharedSpellChecker] upfront in a - // controlled environment: - [NSSpellChecker sharedSpellChecker]; + SalInstance::MacStartupWorkarounds(); } SalYieldMutex::SalYieldMutex() diff --git a/vcl/quartz/cgutils.mm b/vcl/quartz/cgutils.mm index 50b7fcd6540c..f32df8f44772 100644 --- a/vcl/quartz/cgutils.mm +++ b/vcl/quartz/cgutils.mm @@ -20,6 +20,7 @@ #include <quartz/cgutils.h> #include <salbmp.hxx> +#include <salinst.hxx> #ifdef MACOSX #include <osx/saldata.hxx> #else @@ -136,6 +137,31 @@ bool DefaultMTLDeviceIsSupported() return bRet; } +void SalInstance::MacStartupWorkarounds() +{ + // HACK: When the first call to [NSSpellChecker sharedSpellChecker] (in + // lingucomponent/source/spellcheck/macosxspell/macspellimp.mm) is done both on a thread other + // than the main thread and with the SolarMutex erroneously locked, then that can lead to + // deadlock as [NSSpellChecker sharedSpellChecker] internally calls + // AppKit`-[NSSpellChecker init] -> + // AppKit`-[NSSpellChecker _fillSpellCheckerPopupButton:] -> + // AppKit`-[NSApplication(NSServicesMenuPrivate) _fillSpellCheckerPopupButton:] -> + // AppKit`-[NSMenu insertItem:atIndex:] -> + // Foundation`-[NSNotificationCenter postNotificationName:object:userInfo:] -> + // CoreFoundation`_CFXNotificationPost -> + // Foundation`-[NSOperation waitUntilFinished] + // waiting for work to be done on the main thread, but the main thread is typically already + // blocked (in some event handling loop) waiting to acquire the SolarMutex. The real solution + // would be to fix all the cases where a call to [NSSpellChecker sharedSpellChecker] in + // lingucomponent/source/spellcheck/macosxspell/macspellimp.mm is done while the SolarMutex is + // locked (somewhere up the call chain), but that appears to be rather difficult (see e.g. + // <https://bugs.documentfoundation.org/show_bug.cgi?id=151894> "FILEOPEN a Base Document with + // customized event for open a startform by 'open document' LO stuck"). So, at least for now, + // chicken out and do that first call to [NSSpellChecker sharedSpellChecker] upfront in a + // controlled environment: + [NSSpellChecker sharedSpellChecker]; +} + #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
