summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan-Marek Glogowski <glogow@fbihome.de>2017-01-25 15:53:15 +0100
committerJan-Marek Glogowski <glogow@fbihome.de>2017-07-13 12:10:23 +0200
commit917be98e3f277960635ac66bcea510c2454c80d6 (patch)
tree18def396ae400eb1151211c863d44be457a7831a
parent23beae53b43484d82949019a3279362c7e1dfb4b (diff)
Round-robin invoked tasks
Add some round-robin to the task processing, so equal priority (auto) tasks won't always be scheduled, if there are multiple tasks with the same priority. Change-Id: Ice111aa5f85e9181b3ee9799ca4df0d58f210fe9
-rw-r--r--include/vcl/idle.hxx16
-rw-r--r--vcl/qa/cppunit/timer.cxx42
-rw-r--r--vcl/source/app/idle.cxx5
-rw-r--r--vcl/source/app/scheduler.cxx45
4 files changed, 97 insertions, 11 deletions
diff --git a/include/vcl/idle.hxx b/include/vcl/idle.hxx
index 157d14d96816..cb96c09864c5 100644
--- a/include/vcl/idle.hxx
+++ b/include/vcl/idle.hxx
@@ -46,6 +46,22 @@ public:
virtual void Start() override;
};
+/**
+ * An auto-idle is long running task processing small chunks of data, which
+ * is re-scheduled multiple times.
+ *
+ * Remember to stop the Idle when finished, as it would otherwise busy loop the CPU!
+ *
+ * It probably makes sense to re-implement ReadyForSchedule and UpdateMinPeriod,
+ * in case there is a quick check and it can otherwise sleep.
+ */
+class VCL_DLLPUBLIC AutoIdle : public Idle
+{
+public:
+ AutoIdle( const sal_Char *pDebugName = nullptr );
+};
+
+
#endif // INCLUDED_VCL_IDLE_HXX
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/qa/cppunit/timer.cxx b/vcl/qa/cppunit/timer.cxx
index 83b0a7cc01e5..dcd11319e293 100644
--- a/vcl/qa/cppunit/timer.cxx
+++ b/vcl/qa/cppunit/timer.cxx
@@ -70,6 +70,7 @@ public:
void testTriggerIdleFromIdle();
void testInvokedReStart();
void testPriority();
+ void testRoundRobin();
CPPUNIT_TEST_SUITE(TimerTest);
CPPUNIT_TEST(testIdle);
@@ -88,6 +89,7 @@ public:
CPPUNIT_TEST(testTriggerIdleFromIdle);
CPPUNIT_TEST(testInvokedReStart);
CPPUNIT_TEST(testPriority);
+ CPPUNIT_TEST(testRoundRobin);
CPPUNIT_TEST_SUITE_END();
};
@@ -487,6 +489,46 @@ void TimerTest::testPriority()
}
}
+
+class TestAutoIdleRR : public AutoIdle
+{
+ sal_uInt32 &mrCount;
+
+ DECL_LINK( IdleRRHdl, Timer *, void );
+
+public:
+ TestAutoIdleRR( sal_uInt32 &rCount,
+ const sal_Char *pDebugName )
+ : AutoIdle( pDebugName )
+ , mrCount( rCount )
+ {
+ CPPUNIT_ASSERT_EQUAL( mrCount, sal_uInt32(0) );
+ SetInvokeHandler( LINK( this, TestAutoIdleRR, IdleRRHdl ) );
+ Start();
+ }
+};
+
+IMPL_LINK_NOARG(TestAutoIdleRR, IdleRRHdl, Timer *, void)
+{
+ ++mrCount;
+ if ( mrCount == 3 )
+ Stop();
+}
+
+void TimerTest::testRoundRobin()
+{
+ sal_uInt32 nCount1 = 0, nCount2 = 0;
+ TestAutoIdleRR aIdle1( nCount1, "TestAutoIdleRR aIdle1" ),
+ aIdle2( nCount2, "TestAutoIdleRR aIdle2" );
+ while ( Application::Reschedule() )
+ {
+ CPPUNIT_ASSERT( nCount1 == nCount2 || nCount1 - 1 == nCount2 );
+ CPPUNIT_ASSERT( nCount1 <= 3 );
+ CPPUNIT_ASSERT( nCount2 <= 3 );
+ }
+ CPPUNIT_ASSERT( 3 == nCount1 && 3 == nCount2 );
+}
+
CPPUNIT_TEST_SUITE_REGISTRATION(TimerTest);
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/vcl/source/app/idle.cxx b/vcl/source/app/idle.cxx
index 18f12076710d..8038005a7d4f 100644
--- a/vcl/source/app/idle.cxx
+++ b/vcl/source/app/idle.cxx
@@ -67,4 +67,9 @@ sal_uInt64 Idle::UpdateMinPeriod( sal_uInt64 /* nMinPeriod */, sal_uInt64 /* nTi
return Scheduler::ImmediateTimeoutMs;
}
+AutoIdle::AutoIdle( const sal_Char *pDebugName )
+ : Idle( true, pDebugName )
+{
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/app/scheduler.cxx b/vcl/source/app/scheduler.cxx
index 04938d1dd3a3..c2e9bccff116 100644
--- a/vcl/source/app/scheduler.cxx
+++ b/vcl/source/app/scheduler.cxx
@@ -234,6 +234,22 @@ static inline void AppendSchedulerData( ImplSchedulerContext &rSchedCtx,
pSchedulerData->mpNext = nullptr;
}
+static inline ImplSchedulerData* DropSchedulerData(
+ ImplSchedulerContext &rSchedCtx, ImplSchedulerData * const pPrevSchedulerData,
+ ImplSchedulerData * const pSchedulerData )
+{
+ assert( !pPrevSchedulerData || (pPrevSchedulerData->mpNext == pSchedulerData) );
+
+ ImplSchedulerData * const pSchedulerDataNext = pSchedulerData->mpNext;
+ if ( pPrevSchedulerData )
+ pPrevSchedulerData->mpNext = pSchedulerDataNext;
+ else
+ rSchedCtx.mpFirstSchedulerData = pSchedulerDataNext;
+ if ( !pSchedulerDataNext )
+ rSchedCtx.mpLastSchedulerData = pPrevSchedulerData;
+ return pSchedulerDataNext;
+}
+
bool Scheduler::ProcessTaskScheduling()
{
ImplSVData *pSVData = ImplGetSVData();
@@ -246,6 +262,7 @@ bool Scheduler::ProcessTaskScheduling()
ImplSchedulerData* pSchedulerData = nullptr;
ImplSchedulerData* pPrevSchedulerData = nullptr;
ImplSchedulerData *pMostUrgent = nullptr;
+ ImplSchedulerData *pPrevMostUrgent = nullptr;
sal_uInt64 nMinPeriod = InfiniteTimeoutMs;
DBG_TESTSOLARMUTEX();
@@ -268,13 +285,8 @@ bool Scheduler::ProcessTaskScheduling()
// Should the Task be released from scheduling or stacked?
if ( pSchedulerData->mbDelete || !pSchedulerData->mpTask || pSchedulerData->mbInScheduler )
{
- ImplSchedulerData * const pSchedulerDataNext = pSchedulerData->mpNext;
- if ( pPrevSchedulerData )
- pPrevSchedulerData->mpNext = pSchedulerDataNext;
- else
- rSchedCtx.mpFirstSchedulerData = pSchedulerDataNext;
- if ( !pSchedulerDataNext )
- rSchedCtx.mpLastSchedulerData = pPrevSchedulerData;
+ ImplSchedulerData * const pSchedulerDataNext =
+ DropSchedulerData( rSchedCtx, pPrevSchedulerData, pSchedulerData );
if ( pSchedulerData->mbInScheduler )
{
pSchedulerData->mpNext = rSchedCtx.mpSchedulerStack;
@@ -300,6 +312,7 @@ bool Scheduler::ProcessTaskScheduling()
{
if ( pMostUrgent )
UpdateMinPeriod( pMostUrgent, nTime, nMinPeriod );
+ pPrevMostUrgent = pPrevSchedulerData;
pMostUrgent = pSchedulerData;
}
else
@@ -344,11 +357,21 @@ next_entry:
UpdateSystemTimer( rSchedCtx, ImmediateTimeoutMs, true,
tools::Time::GetSystemTicks() );
}
- else if ( pMostUrgent->mpTask && !pMostUrgent->mbDelete )
+ else
{
- pMostUrgent->mnUpdateTime = tools::Time::GetSystemTicks();
- UpdateMinPeriod( pMostUrgent, nTime, nMinPeriod );
- UpdateSystemTimer( rSchedCtx, nMinPeriod, false, nTime );
+ // Since we can restart tasks, round-robin all non-last tasks
+ if ( pMostUrgent->mpNext )
+ {
+ DropSchedulerData( rSchedCtx, pPrevMostUrgent, pMostUrgent );
+ AppendSchedulerData( rSchedCtx, pMostUrgent );
+ }
+
+ if ( pMostUrgent->mpTask && !pMostUrgent->mbDelete )
+ {
+ pMostUrgent->mnUpdateTime = nTime;
+ UpdateMinPeriod( pMostUrgent, nTime, nMinPeriod );
+ UpdateSystemTimer( rSchedCtx, nMinPeriod, false, nTime );
+ }
}
}