diff options
-rw-r--r-- | framework/CppunitTest_framework_services.mk | 40 | ||||
-rw-r--r-- | framework/Module_framework.mk | 7 | ||||
-rw-r--r-- | framework/qa/cppunit/services.cxx | 133 | ||||
-rw-r--r-- | framework/source/services/frame.cxx | 7 |
4 files changed, 187 insertions, 0 deletions
diff --git a/framework/CppunitTest_framework_services.mk b/framework/CppunitTest_framework_services.mk new file mode 100644 index 000000000000..f098b7e0bcf9 --- /dev/null +++ b/framework/CppunitTest_framework_services.mk @@ -0,0 +1,40 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_CppunitTest_CppunitTest,framework_services)) + +$(eval $(call gb_CppunitTest_add_exception_objects,framework_services, \ + framework/qa/cppunit/services \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,framework_services)) + +$(eval $(call gb_CppunitTest_use_libraries,framework_services, \ + comphelper \ + cppu \ + cppuhelper \ + sal \ + salhelper \ + test \ + unotest \ + vcl \ +)) + +$(eval $(call gb_CppunitTest_use_external,framework_services,boost_headers)) + +$(eval $(call gb_CppunitTest_use_sdk_api,framework_services)) + +$(eval $(call gb_CppunitTest_use_ure,framework_services)) +$(eval $(call gb_CppunitTest_use_vcl,framework_services)) + +$(eval $(call gb_CppunitTest_use_rdb,framework_services,services)) + +$(eval $(call gb_CppunitTest_use_configuration,framework_services)) + +# vim: set noet sw=4 ts=4: diff --git a/framework/Module_framework.mk b/framework/Module_framework.mk index 4baf948abd9d..2d59a8183456 100644 --- a/framework/Module_framework.mk +++ b/framework/Module_framework.mk @@ -29,6 +29,13 @@ $(eval $(call gb_Module_add_slowcheck_targets,framework,\ CppunitTest_framework_dispatch \ )) +# Not sure why this is not stable on macOS. +ifneq ($(OS),MACOSX) +$(eval $(call gb_Module_add_slowcheck_targets,framework,\ + CppunitTest_framework_services \ +)) +endif + $(eval $(call gb_Module_add_l10n_targets,framework,\ AllLangMoTarget_fwk \ )) diff --git a/framework/qa/cppunit/services.cxx b/framework/qa/cppunit/services.cxx new file mode 100644 index 000000000000..93d05ff65ca5 --- /dev/null +++ b/framework/qa/cppunit/services.cxx @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + */ + +#include <test/bootstrapfixture.hxx> +#include <unotest/macros_test.hxx> + +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XComponentLoader.hpp> +#include <com/sun/star/frame/FrameSearchFlag.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <salhelper/thread.hxx> +#include <vcl/svapp.hxx> +#include <vcl/scheduler.hxx> +#include <vcl/wrkwin.hxx> + +using namespace ::com::sun::star; + +namespace +{ +/// Covers framework/source/services/ fixes. +class Test : public test::BootstrapFixture, public unotest::MacrosTest +{ +protected: + uno::Reference<lang::XComponent> mxComponent; + +public: + void setUp() override; + void tearDown() override; + uno::Reference<lang::XComponent>& getComponent() { return mxComponent; } +}; + +void Test::setUp() +{ + test::BootstrapFixture::setUp(); + + mxDesktop.set(frame::Desktop::create(mxComponentContext)); +} + +void Test::tearDown() +{ + if (mxComponent.is()) + mxComponent->dispose(); + + test::BootstrapFixture::tearDown(); +} + +/// Invokes XFrameImpl::loadComponentFromURL() on a thread. +class TestThread : public salhelper::Thread +{ + uno::Reference<frame::XComponentLoader> mxComponentLoader; + uno::Reference<lang::XComponent>& mrComponent; + +public: + TestThread(const uno::Reference<frame::XComponentLoader>& xComponentLoader, + uno::Reference<lang::XComponent>& rComponent); + void execute() override; +}; + +TestThread::TestThread(const uno::Reference<frame::XComponentLoader>& xComponentLoader, + uno::Reference<lang::XComponent>& rComponent) + : salhelper::Thread("TestThread") + , mxComponentLoader(xComponentLoader) + , mrComponent(rComponent) +{ +} + +void TestThread::execute() +{ + sal_Int32 nSearchFlags = frame::FrameSearchFlag::AUTO; + uno::Sequence<beans::PropertyValue> aArguments = { + comphelper::makePropertyValue("OnMainThread", true), + }; + // Note how this is invoking loadComponentFromURL() on a frame, not on the desktop, as usual. + mrComponent = mxComponentLoader->loadComponentFromURL("private:factory/swriter", "_self", + nSearchFlags, aArguments); +} + +CPPUNIT_TEST_FIXTURE(Test, testLoadComponentFromURL) +{ + // Without the accompanying fix in place, this test would have failed with: + // thread 1: comphelper::SolarMutex::doRelease end: m_nCount is 1 + // thread 2: vcl::SolarThreadExecutor::execute: before SolarMutexReleaser ctor + // thread 2: comphelper::SolarMutex::doRelease start: m_nCount is 1, bUnlockAll is 1 + // thread 2: comphelper::SolarMutex::doRelease: failed IsCurrentThread() check, will abort + // Notice how thread 2 attempts to release the solar mutex while thread 1 holds it. + + // Create a default window, so by the time the thread would post a user event, it doesn't need + // the solar mutex to process a SendMessageW() call on Windows. + VclPtrInstance<WorkWindow> xWindow(nullptr, WB_APP | WB_STDWORK); + // Variable is not used, it holds the default window. + (void)xWindow; + + rtl::Reference<TestThread> xThread; + { + // Start the thread that will load the component, but hold the solar mutex for now, so we + // can see if it blocks. + SolarMutexGuard guard; + uno::Reference<frame::XFrame> xFrame = mxDesktop->findFrame("_blank", /*nSearchFlags=*/0); + uno::Reference<frame::XComponentLoader> xComponentLoader(xFrame, uno::UNO_QUERY); + xThread = new TestThread(xComponentLoader, getComponent()); + xThread->launch(); + // If loadComponentFromURL() doesn't lock the solar mutex, the test will abort here. + osl::Thread::wait(std::chrono::seconds(1)); + } + { + // Now release the solar mutex, so the thread can post the task on the main loop. + SolarMutexReleaser releaser; + osl::Thread::wait(std::chrono::seconds(1)); + } + { + // Spin the main loop. + SolarMutexGuard guard; + Scheduler::ProcessEventsToIdle(); + } + { + // Stop the thread. + SolarMutexReleaser releaser; + xThread->join(); + } +} +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/services/frame.cxx b/framework/source/services/frame.cxx index db71cfdbcbdd..4941566b80f7 100644 --- a/framework/source/services/frame.cxx +++ b/framework/source/services/frame.cxx @@ -583,9 +583,16 @@ css::uno::Reference< css::lang::XComponent > SAL_CALL XFrameImpl::loadComponentF bool bOnMainThread = aDescriptor.getUnpackedValueOrDefault("OnMainThread", false); if (bOnMainThread) + { + // Make sure that we own the solar mutex, otherwise later + // vcl::SolarThreadExecutor::execute() will release the solar mutex, even if it's owned by + // an other thread, leading to an std::abort() at the end. + SolarMutexGuard g; + return vcl::solarthread::syncExecute(std::bind(&LoadEnv::loadComponentFromURL, xThis, m_xContext, sURL, sTargetFrameName, nSearchFlags, lArguments)); + } else return LoadEnv::loadComponentFromURL(xThis, m_xContext, sURL, sTargetFrameName, nSearchFlags, lArguments); |