framework/inc/helper/statusindicatorfactory.hxx | 2 framework/inc/helper/wakeupthread.hxx | 17 -- framework/source/helper/statusindicatorfactory.cxx | 13 - framework/source/helper/wakeupthread.cxx | 152 ++++++++++++++++++--- 4 files changed, 144 insertions(+), 40 deletions(-)
New commits: commit 6e322952a57484c1d823acd5c19797c7d3dea579 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Fri May 24 21:32:27 2024 +0100 Commit: Noel Grandin <noel.gran...@collabora.co.uk> CommitDate: Sat May 25 19:20:58 2024 +0200 WakeupThread - re-factor to have a single shared wakeup thread. The WakeupThread is an attempt to avoid needing to call gettimeofday and/or update a visual status bar very regularly, and to have a thread marking the passing of time to emit progress updates infrequently. Re-factor this to have a single time-keeping thread, so it will be easier to shutdown and re-start for LOK; and also to simplify some of the complexity lurking here. Change-Id: Ia95890e5d6041a029484aa3f7df13b59a0397086 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/168032 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk> diff --git a/framework/inc/helper/statusindicatorfactory.hxx b/framework/inc/helper/statusindicatorfactory.hxx index c492a645f5bc..08f31aa87ada 100644 --- a/framework/inc/helper/statusindicatorfactory.hxx +++ b/framework/inc/helper/statusindicatorfactory.hxx @@ -150,7 +150,7 @@ class StatusIndicatorFactory final : public ::cppu::WeakImplHelper< /** Notify us if a fix time is over. We use it to implement an intelligent "Reschedule" ... */ - rtl::Reference<WakeUpThread> m_pWakeUp; + std::unique_ptr<WakeUpThread> m_pWakeUp; /** Our WakeUpThread calls us in our interface method "XUpdatable::update(). There we set this member m_bAllowReschedule to sal_True. Next time if our impl_reschedule() diff --git a/framework/inc/helper/wakeupthread.hxx b/framework/inc/helper/wakeupthread.hxx index cdc8700a5266..b25a933dc8c8 100644 --- a/framework/inc/helper/wakeupthread.hxx +++ b/framework/inc/helper/wakeupthread.hxx @@ -23,9 +23,6 @@ #include <com/sun/star/uno/Reference.hxx> #include <cppuhelper/weakref.hxx> -#include <condition_variable> -#include <mutex> -#include <salhelper/thread.hxx> namespace com::sun::star::util { @@ -34,19 +31,17 @@ class XUpdatable; namespace framework { -class WakeUpThread final : public salhelper::Thread +/// Provides a regular callback to @updatable roughly every 25ms while running +class WakeUpThread final { - css::uno::WeakReference<css::util::XUpdatable> updatable_; - std::condition_variable condition_; - std::mutex mutex_; - bool terminate_; - - void execute() override; + css::uno::Reference<css::util::XUpdatable> _updatable; public: WakeUpThread(css::uno::Reference<css::util::XUpdatable> const& updatable); - void stop(); + + static void joinThread(); + static void startThread(); }; } diff --git a/framework/source/helper/statusindicatorfactory.cxx b/framework/source/helper/statusindicatorfactory.cxx index 64cf3543c22a..879bd8597b33 100644 --- a/framework/source/helper/statusindicatorfactory.cxx +++ b/framework/source/helper/statusindicatorfactory.cxx @@ -544,24 +544,19 @@ void StatusIndicatorFactory::impl_startWakeUpThread() if (m_bDisableReschedule) return; - if (!m_pWakeUp.is()) - { - m_pWakeUp = new WakeUpThread(this); - m_pWakeUp->launch(); - } + if (!m_pWakeUp) + m_pWakeUp.reset(new WakeUpThread(this)); } void StatusIndicatorFactory::impl_stopWakeUpThread() { - rtl::Reference<WakeUpThread> wakeUp; + std::unique_ptr<WakeUpThread> wakeUp; { std::scoped_lock g(m_mutex); std::swap(wakeUp, m_pWakeUp); } - if (wakeUp.is()) - { + if (wakeUp) wakeUp->stop(); - } } } // namespace framework diff --git a/framework/source/helper/wakeupthread.cxx b/framework/source/helper/wakeupthread.cxx index 40487c83b88f..63d52a82da76 100644 --- a/framework/source/helper/wakeupthread.cxx +++ b/framework/source/helper/wakeupthread.cxx @@ -21,40 +21,154 @@ #include <com/sun/star/uno/Reference.hxx> #include <com/sun/star/util/XUpdatable.hpp> +#include <rtl/ref.hxx> +#include <osl/thread.hxx> +#include <salhelper/thread.hxx> +#include <condition_variable> +#include <chrono> +#include <vector> +#include <mutex> #include <helper/wakeupthread.hxx> -#include <chrono> using namespace std::chrono_literals; -void framework::WakeUpThread::execute() { - for (;;) { +/// We only need one thread to wake everyone up. + +namespace +{ +class SharedWakeUpThread final : public salhelper::Thread +{ + static std::vector<css::uno::WeakReference<css::util::XUpdatable>> updatables; + std::condition_variable condition; + bool terminate; + +public: + static rtl::Reference<SharedWakeUpThread> wakeupThread; + + static std::mutex& getMutex() + { + static std::mutex mutex; + return mutex; + } + + SharedWakeUpThread() + : Thread("WakeUpThread") + , terminate(false) + { + assert(!wakeupThread); + launch(); + } + + void execute() override + { + while (true) { - std::unique_lock g(mutex_); - condition_.wait_for(g, 25ms, [this] { return terminate_; }); - if (terminate_) { + std::unique_lock g(getMutex()); + condition.wait_for(g, 25ms, [this] { return terminate; }); + if (terminate || updatables.empty()) break; + + auto copyOfUpdatables = updatables; + g.unlock(); + + for (auto& it : copyOfUpdatables) + { + css::uno::Reference<css::util::XUpdatable> up(it); + if (up.is()) // check weak + up->update(); } } - css::uno::Reference<css::util::XUpdatable> up(updatable_); - if (up.is()) { - up->update(); + + std::unique_lock g(getMutex()); + if (updatables.empty()) + { + terminate = false; + wakeupThread.clear(); } } -} -framework::WakeUpThread::WakeUpThread( - css::uno::Reference<css::util::XUpdatable> const & updatable): - Thread("WakeUpThread"), updatable_(updatable), terminate_(false) -{} + static void startThread() + { + std::unique_lock g(getMutex()); + if (!updatables.empty() && !wakeupThread) + wakeupThread = new SharedWakeUpThread(); + } -void framework::WakeUpThread::stop() { + void stopWithLock(std::unique_lock<std::mutex>& g) { - std::unique_lock g(mutex_); - terminate_ = true; + terminate = true; + condition.notify_one(); + g.unlock(); + + join(); } - condition_.notify_one(); - join(); + + static void disposeThreadWithLock(std::unique_lock<std::mutex>& g) + { + if (wakeupThread) + { + auto holdRef = wakeupThread; + wakeupThread.clear(); + holdRef->stopWithLock(g); + } + assert(!wakeupThread); + } + + static void add(css::uno::WeakReference<css::util::XUpdatable> up) + { + std::unique_lock g(getMutex()); + updatables.push_back(up); + if (!wakeupThread) + wakeupThread = new SharedWakeUpThread(); + } + + static void remove(css::uno::WeakReference<css::util::XUpdatable> up) + { + std::unique_lock g(getMutex()); + auto it = updatables.begin(); + bool found = false; + for (; it != updatables.end(); ++it) + { + css::uno::Reference<css::util::XUpdatable> itValid(*it); + if (!itValid || *it == up) + { + it = updatables.erase(it); + found = true; + break; + } + } + (void)found; assert(found); + if (updatables.empty()) + disposeThreadWithLock(g); + } + + static void joinThread() + { + std::unique_lock g(getMutex()); + disposeThreadWithLock(g); + } +}; + +rtl::Reference<SharedWakeUpThread> SharedWakeUpThread::wakeupThread; +std::vector<css::uno::WeakReference<css::util::XUpdatable>> SharedWakeUpThread::updatables; } +namespace framework +{ +/* static */ void WakeUpThread::startThread() { SharedWakeUpThread::startThread(); } + +WakeUpThread::WakeUpThread(css::uno::Reference<css::util::XUpdatable> const& up) + : _updatable(up) +{ + assert(_updatable); + SharedWakeUpThread::add(_updatable); +} + +void WakeUpThread::stop() { SharedWakeUpThread::remove(_updatable); } + +/* static */ void WakeUpThread::joinThread() { SharedWakeUpThread::joinThread(); } + +} // namespace framework + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */