diff options
author | Jan-Marek Glogowski <glogow@fbihome.de> | 2019-07-07 23:09:15 +0200 |
---|---|---|
committer | Jan-Marek Glogowski <glogow@fbihome.de> | 2019-08-13 16:53:07 +0200 |
commit | 6e13585508ca3c9b66c6571ad1eb42bfcb66ef0b (patch) | |
tree | df44551b7d3182036117c10ff694d1ae5905264f /include | |
parent | 536ab2f3ba1e71badaaf98db9419ca7c7ddd7ac4 (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.hxx | 123 |
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: */ |