summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorJan-Marek Glogowski <glogow@fbihome.de>2019-07-07 23:09:15 +0200
committerJan-Marek Glogowski <glogow@fbihome.de>2019-08-13 16:53:07 +0200
commit6e13585508ca3c9b66c6571ad1eb42bfcb66ef0b (patch)
treedf44551b7d3182036117c10ff694d1ae5905264f /include
parent536ab2f3ba1e71badaaf98db9419ca7c7ddd7ac4 (diff)
Add a TaskStopwatch to interrupt idle loops
If we have multiple pending Idles, they will interrupt / starve each other, because there will be an instant pending timeout for the next Idle. This patch introduces a time slice to tasks, so long running events can use a TaskStopwatch to do the real interrupt after running out of their time slice. Apart from the time, this breaks when AnyInput is available, except for the timer event. This class just helps to track the time, as the scheduler is coop, not preemptive. Change-Id: I9d0b4a5aa388ebdf496b355d100152d890224524 Reviewed-on: https://gerrit.libreoffice.org/75568 Tested-by: Jenkins Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
Diffstat (limited to 'include')
-rw-r--r--include/vcl/TaskStopwatch.hxx123
1 files changed, 123 insertions, 0 deletions
diff --git a/include/vcl/TaskStopwatch.hxx b/include/vcl/TaskStopwatch.hxx
new file mode 100644
index 000000000000..c98dcc6f5527
--- /dev/null
+++ b/include/vcl/TaskStopwatch.hxx
@@ -0,0 +1,123 @@
+/* -*- 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/.
+ */
+
+#ifndef INCLUDED_VCL_TASK_STOPWATCH_HXX
+#define INCLUDED_VCL_TASK_STOPWATCH_HXX
+
+#include <tools/time.hxx>
+#include <vcl/dllapi.h>
+#include <vcl/inputtypes.hxx>
+#include <vcl/svapp.hxx>
+
+/**
+ * Helper class primary used to track time of long running iterating tasks.
+ *
+ * Normally it should be sufficiant to instanciate the watch object before
+ * starting the iteration and query continueIter() at the end of each.
+ *
+ * Called Stopwatch, because there is already a Timer class in the Scheduler.
+ *
+ * TODO: merge into the general Scheduler, so this can also be used to track
+ * Task runtimes in a more general way.
+ * TODO: handle fast iterations, where continueIter is called multiple times
+ * per tick, by counting the iterations per tick and use that for appoximation.
+ **/
+class VCL_DLLPUBLIC TaskStopwatch
+{
+ static constexpr VclInputFlags eDefaultInputStop = VCL_INPUT_ANY & ~VclInputFlags::TIMER;
+ static constexpr unsigned int nDefaultTimeSlice = 50;
+ static unsigned int m_nTimeSlice;
+
+ sal_uInt64 m_nStartTicks;
+ sal_uInt64 m_nIterStartTicks;
+ bool m_bConsiderLastIterTime;
+ VclInputFlags m_eInputStop;
+
+ bool nextIter(bool bQueryOnly)
+ {
+ sal_uInt64 nCurTicks = tools::Time::GetSystemTicks();
+ // handle system ticks wrap as exceeded time slice
+ if (nCurTicks < m_nStartTicks)
+ return false;
+
+ if (!bQueryOnly && m_bConsiderLastIterTime)
+ {
+ // based on the last iter runtime, we don't expect to finish in time
+ // m_nTimeSlice < (nCurTicks - m_nStartTicks) + (nCurTicks - m_nIterStartTicks)
+ if (m_nTimeSlice < 2 * nCurTicks - m_nIterStartTicks - m_nStartTicks)
+ return false;
+ }
+ // time slice exceeded
+ else if (m_nTimeSlice < nCurTicks - m_nStartTicks)
+ return false;
+
+ if (!bQueryOnly)
+ m_nIterStartTicks = nCurTicks;
+
+ return !Application::AnyInput(m_eInputStop);
+ }
+
+public:
+ /**
+ * Per default the watch consideres the last iter time when asking for an
+ * other iteration, so considers Scheduler::acceptableTaskTime as a
+ * maximum value.
+ *
+ * If you already know your iter time vary in a large range, consider
+ * setting bConciderLastIterTime to false, so Scheduler::acceptableTaskTime
+ * will be used as a mimimum time slot.
+ **/
+ TaskStopwatch(bool bConciderLastIterTime = true)
+ : m_nStartTicks(tools::Time::GetSystemTicks())
+ , m_nIterStartTicks(m_nStartTicks)
+ , m_bConsiderLastIterTime(bConciderLastIterTime)
+ , m_eInputStop(eDefaultInputStop)
+ {
+ }
+
+ /**
+ * Returns true, if the time slot is already exceeded
+ **/
+ bool exceededRuntime() { return !nextIter(true); }
+
+ /**
+ * Returns true, if an other iteration will probably pass in the time slot
+ **/
+ bool continueIter() { return nextIter(false); }
+
+ /**
+ * Reset the stopwatch
+ **/
+ void reset()
+ {
+ m_nStartTicks = tools::Time::GetSystemTicks();
+ m_nIterStartTicks = m_nStartTicks;
+ }
+
+ /**
+ * Sets the input events, which should also "exceed" the stopwatch.
+ *
+ * Per default this ignores the VclInputFlags::TIMER.
+ */
+ void setInputStop(VclInputFlags eInputStop = eDefaultInputStop) { m_eInputStop = eInputStop; }
+ VclInputFlags inputStop() const { return m_eInputStop; }
+
+ /**
+ * Sets the time considered the acceptable maximum for a task to run
+ *
+ * This is an orientation for long time background jobs to yield to
+ * the scheduler, so Idle task don't starve each other too much.
+ **/
+ static unsigned int timeSlice() { return m_nTimeSlice; }
+ static void setTimeSlice(unsigned int nTimeSlice) { m_nTimeSlice = nTimeSlice; }
+};
+
+#endif // INCLUDED_VCL_TASK_STOPWATCH_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */