include/vcl/scheduler.hxx | 6 +++++ sfx2/qa/cppunit/view.cxx | 23 +++++++++++++++++++++ sfx2/source/view/lokhelper.cxx | 41 +++++++++++++++++++++++++++++--------- vcl/source/app/scheduler.cxx | 44 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 9 deletions(-)
New commits: commit e9e39e45f0daed52276377321088aa4d49b112b6 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Fri Feb 14 14:20:21 2025 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Fri Feb 14 16:02:46 2025 +0100 cool#11064 vcl lok: expose info about the scheduler A LOK client can interrupt the vcl main loop in its 'any input' callback. When making that decision, it's useful in case it can know what's the priority of the most urgent job, for example it may want core to still finish high priority tasks, but not idle ones. Add a LOK API to expose this info. Keep it minimal, so it's realistic to call this frequently from a LOK client. Change-Id: Id51668eb8156067e60d6fd0f33606c65858008a9 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/181668 Reviewed-by: Michael Meeks <michael.me...@collabora.com> Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> diff --git a/include/vcl/scheduler.hxx b/include/vcl/scheduler.hxx index 1b63404139bf..b407037eb581 100644 --- a/include/vcl/scheduler.hxx +++ b/include/vcl/scheduler.hxx @@ -23,6 +23,10 @@ #include <vcl/dllapi.h> struct ImplSchedulerContext; +namespace tools +{ +class JsonWriter; +} class VCL_DLLPUBLIC Scheduler final { @@ -81,6 +85,8 @@ public: static void SetDeterministicMode(bool bDeterministic); /// Return the current state of deterministic mode. static bool GetDeterministicMode(); + + static void dumpAsJSON(tools::JsonWriter& rJsonWriter); }; #endif // INCLUDED_VCL_SCHEDULER_HXX diff --git a/sfx2/qa/cppunit/view.cxx b/sfx2/qa/cppunit/view.cxx index 9a380db1a1d0..b8e5e645600a 100644 --- a/sfx2/qa/cppunit/view.cxx +++ b/sfx2/qa/cppunit/view.cxx @@ -218,6 +218,29 @@ CPPUNIT_TEST_FIXTURE(Sfx2ViewTest, testSignatureSerialize) // provided parameters. CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getSignatureCount()); } + +CPPUNIT_TEST_FIXTURE(Sfx2ViewTest, testScheduler) +{ + // Given an empty document: + mxComponent = loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument"); + + // When asking for the state of the scheduler: + tools::JsonWriter aWriter; + SfxLokHelper::getCommandValues(aWriter, ".uno:Scheduler"); + OString aJson = aWriter.finishAndGetAsOString(); + + // Then make sure we get an int priority: + CPPUNIT_ASSERT(SfxLokHelper::supportsCommand(u"Scheduler")); + std::stringstream aStream{ std::string(aJson) }; + boost::property_tree::ptree aTree; + boost::property_tree::read_json(aStream, aTree); + auto it = aTree.find("mostUrgentPriority"); + // Without the accompanying fix in place, this test would have failed, this JSON key was + // missing. + CPPUNIT_ASSERT(it != aTree.not_found()); + // This returns TaskPriority::HIGH_IDLE, but just make sure we get an int. + it->second.get_value<int>(); +} #endif CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sfx2/source/view/lokhelper.cxx b/sfx2/source/view/lokhelper.cxx index 77d39d22f2e0..cff43e6de576 100644 --- a/sfx2/source/view/lokhelper.cxx +++ b/sfx2/source/view/lokhelper.cxx @@ -43,6 +43,7 @@ #include <tools/json_writer.hxx> #include <svl/cryptosign.hxx> #include <tools/urlobj.hxx> +#include <vcl/scheduler.hxx> #include <boost/property_tree/json_parser.hpp> @@ -994,7 +995,8 @@ void SfxLokHelper::addCertificates(const std::vector<std::string>& rCerts) bool SfxLokHelper::supportsCommand(std::u16string_view rCommand) { - static const std::initializer_list<std::u16string_view> vSupport = { u"Signature" }; + static const std::initializer_list<std::u16string_view> vSupport + = { u"Signature", u"Scheduler" }; return std::find(vSupport.begin(), vSupport.end(), rCommand) != vSupport.end(); } @@ -1028,14 +1030,11 @@ std::map<OUString, OUString> SfxLokHelper::parseCommandParameters(std::u16string return aMap; } -void SfxLokHelper::getCommandValues(tools::JsonWriter& rJsonWriter, std::string_view rCommand) +namespace +{ +/// Implements getCommandValues(".uno:Signature"). +void GetSignature(tools::JsonWriter& rJsonWriter, std::string_view rCommand) { - static constexpr OString aSignature(".uno:Signature"_ostr); - if (!o3tl::starts_with(rCommand, aSignature)) - { - return; - } - SfxObjectShell* pObjectShell = SfxObjectShell::Current(); if (!pObjectShell) { @@ -1053,7 +1052,7 @@ void SfxLokHelper::getCommandValues(tools::JsonWriter& rJsonWriter, std::string_ } pObjectShell->SignDocumentContentUsingCertificate(aSigningContext); // Set commandName, this is a reply to a request. - rJsonWriter.put("commandName", aSignature); + rJsonWriter.put("commandName", ".uno:Signature"); auto aCommandValues = rJsonWriter.startNode("commandValues"); rJsonWriter.put("signatureTime", aSigningContext.m_nSignatureTime); @@ -1064,6 +1063,30 @@ void SfxLokHelper::getCommandValues(tools::JsonWriter& rJsonWriter, std::string_ rJsonWriter.put("digest", aBuffer.makeStringAndClear()); } +/// Implements getCommandValues(".uno:Scheduler"). +void GetScheduler(tools::JsonWriter& rJsonWriter) +{ + Scheduler::dumpAsJSON(rJsonWriter); +} +} + +void SfxLokHelper::getCommandValues(tools::JsonWriter& rJsonWriter, std::string_view rCommand) +{ + static constexpr OString aSignature(".uno:Signature"_ostr); + static constexpr OString aScheduler(".uno:Scheduler"_ostr); + if (o3tl::starts_with(rCommand, aSignature)) + { + GetSignature(rJsonWriter, rCommand); + return; + } + + if (o3tl::starts_with(rCommand, aScheduler)) + { + GetScheduler(rJsonWriter); + return; + } +} + void SfxLokHelper::notifyUpdate(SfxViewShell const* pThisView, int nType) { if (DisableCallbacks::disabled() || !pThisView) diff --git a/vcl/source/app/scheduler.cxx b/vcl/source/app/scheduler.cxx index e6893055f633..bb5436a3a04f 100644 --- a/vcl/source/app/scheduler.cxx +++ b/vcl/source/app/scheduler.cxx @@ -38,6 +38,7 @@ #include <saltimer.hxx> #include <salinst.hxx> #include <comphelper/profilezone.hxx> +#include <tools/json_writer.hxx> #include <schedulerimpl.hxx> namespace { @@ -271,6 +272,49 @@ bool Scheduler::GetDeterministicMode() return g_bDeterministicMode; } +void Scheduler::dumpAsJSON(tools::JsonWriter& rJsonWriter) +{ + // Similar to Scheduler::CallbackTaskScheduling(), figure out the most urgent priority, but + // don't actually invoke any task. + int nMostUrgentPriority = -1; + ImplSVData* pSVData = ImplGetSVData(); + ImplSchedulerContext& rSchedCtx = pSVData->maSchedCtx; + if (!rSchedCtx.mbActive || rSchedCtx.mnTimerPeriod == InfiniteTimeoutMs) + { + rJsonWriter.put("mostUrgentPriority", nMostUrgentPriority); + return; + } + + sal_uInt64 nTime = tools::Time::GetSystemTicks(); + if (nTime < rSchedCtx.mnTimerStart + rSchedCtx.mnTimerPeriod - 1) + { + rJsonWriter.put("mostUrgentPriority", nMostUrgentPriority); + return; + } + + for (int nTaskPriority = 0; nTaskPriority < PRIO_COUNT; ++nTaskPriority) + { + ImplSchedulerData* pSchedulerData = rSchedCtx.mpFirstSchedulerData[nTaskPriority]; + while (pSchedulerData) + { + Task* pTask = pSchedulerData->mpTask; + if (pTask && pTask->IsActive()) + { + // Const, doesn't modify the task. + sal_uInt64 nReadyPeriod = pTask->UpdateMinPeriod(nTime); + if (nReadyPeriod == ImmediateTimeoutMs) + { + nMostUrgentPriority = nTaskPriority; + rJsonWriter.put("mostUrgentPriority", nMostUrgentPriority); + return; + } + } + pSchedulerData = pSchedulerData->mpNext; + } + } + rJsonWriter.put("mostUrgentPriority", nMostUrgentPriority); +} + inline void Scheduler::UpdateSystemTimer( ImplSchedulerContext &rSchedCtx, const sal_uInt64 nMinPeriod, const bool bForce, const sal_uInt64 nTime )