diff options
author | Jan-Marek Glogowski <glogow@fbihome.de> | 2017-01-25 15:53:15 +0100 |
---|---|---|
committer | Jan-Marek Glogowski <glogow@fbihome.de> | 2017-07-13 12:10:23 +0200 |
commit | 917be98e3f277960635ac66bcea510c2454c80d6 (patch) | |
tree | 18def396ae400eb1151211c863d44be457a7831a | |
parent | 23beae53b43484d82949019a3279362c7e1dfb4b (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.hxx | 16 | ||||
-rw-r--r-- | vcl/qa/cppunit/timer.cxx | 42 | ||||
-rw-r--r-- | vcl/source/app/idle.cxx | 5 | ||||
-rw-r--r-- | vcl/source/app/scheduler.cxx | 45 |
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 ); + } } } |