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 9e82453c2d5e075ee2eed8330f5b710e4a7a6df7 Author: Miklos Vajna <[email protected]> AuthorDate: Fri Feb 14 14:20:21 2025 +0100 Commit: Miklos Vajna <[email protected]> CommitDate: Mon Feb 17 09:43:05 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/+/181761 Reviewed-by: Miklos Vajna <[email protected]> Tested-by: Jenkins diff --git a/include/vcl/scheduler.hxx b/include/vcl/scheduler.hxx index 0181e52c33d2..f0002622d884 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 { @@ -82,6 +86,8 @@ public: /// Return the current state of deterministic mode. static bool GetDeterministicMode(); + static void dumpAsJSON(tools::JsonWriter& rJsonWriter); + // Makes sure that idles are not processed, until the guard is destroyed struct VCL_DLLPUBLIC IdlesLockGuard final { diff --git a/sfx2/qa/cppunit/view.cxx b/sfx2/qa/cppunit/view.cxx index 8c5be5920a07..dad9e4a51f45 100644 --- a/sfx2/qa/cppunit/view.cxx +++ b/sfx2/qa/cppunit/view.cxx @@ -221,6 +221,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 f025e2598411..4ec48abfe6d5 100644 --- a/sfx2/source/view/lokhelper.cxx +++ b/sfx2/source/view/lokhelper.cxx @@ -44,6 +44,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> @@ -996,7 +997,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 6234e6355117..eb3acf65c8a4 100644 --- a/vcl/source/app/scheduler.cxx +++ b/vcl/source/app/scheduler.cxx @@ -41,6 +41,7 @@ #include <salinst.hxx> #include <comphelper/emscriptenthreading.hxx> #include <comphelper/profilezone.hxx> +#include <tools/json_writer.hxx> #include <schedulerimpl.hxx> namespace { @@ -301,6 +302,49 @@ Scheduler::IdlesLockGuard::~IdlesLockGuard() osl_atomic_decrement(&rSchedCtx.mnIdlesLockCount); } +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 )
