diff options
-rw-r--r-- | include/vcl/task.hxx | 12 | ||||
-rw-r--r-- | svx/source/sidebar/PanelLayout.cxx | 7 | ||||
-rw-r--r-- | vcl/inc/svdata.hxx | 6 | ||||
-rw-r--r-- | vcl/qa/cppunit/timer.cxx | 21 | ||||
-rw-r--r-- | vcl/source/app/scheduler.cxx | 208 | ||||
-rw-r--r-- | vcl/source/app/svapp.cxx | 21 |
6 files changed, 158 insertions, 117 deletions
diff --git a/include/vcl/task.hxx b/include/vcl/task.hxx index 7c450099c830..76d2890055fb 100644 --- a/include/vcl/task.hxx +++ b/include/vcl/task.hxx @@ -21,6 +21,7 @@ #define INCLUDED_VCL_TASK_HXX #include <vcl/dllapi.h> +#include <sal/log.hxx> #include <memory> class Scheduler; @@ -38,6 +39,8 @@ enum class TaskPriority LOWEST ///< Low, very idle cleanup tasks }; +#define PRIO_COUNT (static_cast<int>(TaskPriority::LOWEST) + 1) + class VCL_DLLPUBLIC Task { friend class Scheduler; @@ -76,7 +79,7 @@ public: virtual ~Task() COVERITY_NOEXCEPT_FALSE; Task& operator=( const Task& rTask ); - void SetPriority(TaskPriority ePriority) { mePriority = ePriority; } + inline void SetPriority(TaskPriority ePriority); TaskPriority GetPriority() const { return mePriority; } void SetDebugName( const sal_Char *pDebugName ) { mpDebugName = pDebugName; } @@ -100,6 +103,13 @@ public: bool IsStatic() const { return mbStatic; } }; +inline void Task::SetPriority(TaskPriority ePriority) +{ + SAL_WARN_IF(mpSchedulerData, "vcl.schedule", + "Priority will just change after next schedule!"); + mePriority = ePriority; +} + #endif // INCLUDED_VCL_TASK_HXX /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sidebar/PanelLayout.cxx b/svx/source/sidebar/PanelLayout.cxx index 90bbd328da8b..82a7316c0589 100644 --- a/svx/source/sidebar/PanelLayout.cxx +++ b/svx/source/sidebar/PanelLayout.cxx @@ -18,14 +18,15 @@ using namespace sfx2::sidebar; PanelLayout::PanelLayout(vcl::Window* pParent, const OString& rID, const OUString& rUIXMLDescription, const css::uno::Reference<css::frame::XFrame> &rFrame) : Control(pParent) - , m_aPanelLayoutIdle("svx sidebar PanelLayoutIdle") , m_bInClose(false) { SetStyle(GetStyle() | WB_DIALOGCONTROL); - m_pUIBuilder.reset(new VclBuilder(this, getUIRootDir(), rUIXMLDescription, rID, rFrame)); m_aPanelLayoutIdle.SetPriority(TaskPriority::RESIZE); m_aPanelLayoutIdle.SetInvokeHandler( LINK( this, PanelLayout, ImplHandlePanelLayoutTimerHdl ) ); - m_aPanelLayoutIdle.SetDebugName( "svx::PanelLayout m_aPanelLayoutIdle" ); + m_aPanelLayoutIdle.SetDebugName("svx::PanelLayout m_aPanelLayoutIdle"); + + // VclBuilder will trigger resize and start Idle + m_pUIBuilder.reset(new VclBuilder(this, getUIRootDir(), rUIXMLDescription, rID, rFrame)); if (GetSettings().GetStyleSettings().GetAutoMnemonic()) Accelerator::GenerateAutoMnemonicsOnHierarchy(this); } diff --git a/vcl/inc/svdata.hxx b/vcl/inc/svdata.hxx index 6ef279586ea1..6df874abc74b 100644 --- a/vcl/inc/svdata.hxx +++ b/vcl/inc/svdata.hxx @@ -26,6 +26,7 @@ #include <unotools/options.hxx> #include <vcl/svapp.hxx> #include <vcl/window.hxx> +#include <vcl/task.hxx> #include <com/sun/star/lang/XComponent.hpp> #include <com/sun/star/i18n/XCharacterClassification.hpp> @@ -327,9 +328,10 @@ struct BlendFrameCache struct ImplSchedulerContext { - ImplSchedulerData* mpFirstSchedulerData = nullptr; ///< list of all active tasks - ImplSchedulerData* mpLastSchedulerData = nullptr; ///< last item of the mpFirstSchedulerData list + ImplSchedulerData* mpFirstSchedulerData[PRIO_COUNT] = { nullptr, }; ///< list of all active tasks per priority + ImplSchedulerData* mpLastSchedulerData[PRIO_COUNT] = { nullptr, }; ///< last item of each mpFirstSchedulerData list ImplSchedulerData* mpSchedulerStack = nullptr; ///< stack of invoked tasks + ImplSchedulerData* mpSchedulerStackTop = nullptr; ///< top most stack entry to detect needed rescheduling during pop SalTimer* mpSalTimer = nullptr; ///< interface to sal event loop / system timer sal_uInt64 mnTimerStart = 0; ///< start time of the timer sal_uInt64 mnTimerPeriod = SAL_MAX_UINT64; ///< current timer period diff --git a/vcl/qa/cppunit/timer.cxx b/vcl/qa/cppunit/timer.cxx index 4c4ed630c601..cb5a3bd64c52 100644 --- a/vcl/qa/cppunit/timer.cxx +++ b/vcl/qa/cppunit/timer.cxx @@ -453,12 +453,13 @@ class IdleSerializer : public Idle sal_uInt32 const mnPosition; sal_uInt32 &mrProcesed; public: - IdleSerializer( const sal_Char *pDebugName, - sal_uInt32 nPosition, sal_uInt32 &rProcesed ) + IdleSerializer(const sal_Char *pDebugName, TaskPriority ePrio, + sal_uInt32 nPosition, sal_uInt32 &rProcesed) : Idle( pDebugName ) , mnPosition( nPosition ) , mrProcesed( rProcesed ) { + SetPriority(ePrio); Start(); } virtual void Invoke() override @@ -474,10 +475,10 @@ void TimerTest::testPriority() { // Start: 1st Idle low, 2nd high sal_uInt32 nProcessed = 0; - IdleSerializer aLowPrioIdle( "IdleSerializer LowPrio", 2, nProcessed ); - aLowPrioIdle.SetPriority( TaskPriority::LOWEST ); - IdleSerializer aHighPrioIdle( "IdleSerializer HighPrio", 1, nProcessed ); - aHighPrioIdle.SetPriority( TaskPriority::HIGHEST ); + IdleSerializer aLowPrioIdle("IdleSerializer LowPrio", + TaskPriority::LOWEST, 2, nProcessed); + IdleSerializer aHighPrioIdle("IdleSerializer HighPrio", + TaskPriority::HIGHEST, 1, nProcessed); Scheduler::ProcessEventsToIdle(); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Not all idles processed", sal_uInt32(2), nProcessed ); } @@ -485,10 +486,10 @@ void TimerTest::testPriority() { // Start: 1st Idle high, 2nd low sal_uInt32 nProcessed = 0; - IdleSerializer aHighPrioIdle( "IdleSerializer HighPrio", 1, nProcessed ); - aHighPrioIdle.SetPriority( TaskPriority::HIGHEST ); - IdleSerializer aLowPrioIdle( "IdleSerializer LowPrio", 2, nProcessed ); - aLowPrioIdle.SetPriority( TaskPriority::LOWEST ); + IdleSerializer aHighPrioIdle("IdleSerializer HighPrio", + TaskPriority::HIGHEST, 1, nProcessed); + IdleSerializer aLowPrioIdle("IdleSerializer LowPrio", + TaskPriority::LOWEST, 2, nProcessed); Scheduler::ProcessEventsToIdle(); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Not all idles processed", sal_uInt32(2), nProcessed ); } diff --git a/vcl/source/app/scheduler.cxx b/vcl/source/app/scheduler.cxx index 9e5c3bc8db72..506638cc0ee5 100644 --- a/vcl/source/app/scheduler.cxx +++ b/vcl/source/app/scheduler.cxx @@ -104,18 +104,20 @@ void Scheduler::ImplDeInitScheduler() SchedulerGuard aSchedulerGuard; + int nTaskPriority = 0; #if OSL_DEBUG_LEVEL > 0 + sal_uInt32 nTasks = 0; + for (nTaskPriority = 0; nTaskPriority < PRIO_COUNT; ++nTaskPriority) { - ImplSchedulerData* pSchedulerData = rSchedCtx.mpFirstSchedulerData; - sal_uInt32 nTasks = 0; + ImplSchedulerData* pSchedulerData = rSchedCtx.mpFirstSchedulerData[nTaskPriority]; while ( pSchedulerData ) { ++nTasks; pSchedulerData = pSchedulerData->mpNext; } - SAL_INFO( "vcl.schedule.deinit", - "DeInit the scheduler - pending tasks: " << nTasks ); } + SAL_INFO( "vcl.schedule.deinit", + "DeInit the scheduler - pending tasks: " << nTasks ); // clean up all the sfx::SfxItemDisruptor_Impl Idles ProcessEventsToIdle(); @@ -131,7 +133,11 @@ void Scheduler::ImplDeInitScheduler() #if OSL_DEBUG_LEVEL > 0 sal_uInt32 nActiveTasks = 0, nIgnoredTasks = 0; #endif - ImplSchedulerData* pSchedulerData = rSchedCtx.mpFirstSchedulerData; + nTaskPriority = 0; + ImplSchedulerData* pSchedulerData = nullptr; + +next_priority: + pSchedulerData = rSchedCtx.mpFirstSchedulerData[nTaskPriority]; while ( pSchedulerData ) { Task *pTask = pSchedulerData->mpTask; @@ -176,6 +182,11 @@ void Scheduler::ImplDeInitScheduler() pSchedulerData = pSchedulerData->mpNext; delete pDeleteSchedulerData; } + + ++nTaskPriority; + if (nTaskPriority < PRIO_COUNT) + goto next_priority; + #if OSL_DEBUG_LEVEL > 0 SAL_INFO( "vcl.schedule.deinit", "DeInit the scheduler - finished" ); SAL_WARN_IF( 0 != nActiveTasks, "vcl.schedule.deinit", "DeInit active tasks: " @@ -183,8 +194,11 @@ void Scheduler::ImplDeInitScheduler() // assert( nIgnoredTasks == nActiveTasks ); #endif - rSchedCtx.mpFirstSchedulerData = nullptr; - rSchedCtx.mpLastSchedulerData = nullptr; + for (nTaskPriority = 0; nTaskPriority < PRIO_COUNT; ++nTaskPriority) + { + rSchedCtx.mpFirstSchedulerData[nTaskPriority] = nullptr; + rSchedCtx.mpLastSchedulerData[nTaskPriority] = nullptr; + } rSchedCtx.mnTimerPeriod = InfiniteTimeoutMs; } @@ -298,38 +312,40 @@ inline void Scheduler::UpdateSystemTimer( ImplSchedulerContext &rSchedCtx, } static inline void AppendSchedulerData( ImplSchedulerContext &rSchedCtx, - ImplSchedulerData * const pSchedulerData ) + ImplSchedulerData * const pSchedulerData) { - if ( !rSchedCtx.mpLastSchedulerData ) + assert(pSchedulerData->mpTask); + const int nTaskPriority = static_cast<int>(pSchedulerData->mpTask->GetPriority()); + if (!rSchedCtx.mpLastSchedulerData[nTaskPriority]) { - rSchedCtx.mpFirstSchedulerData = pSchedulerData; - rSchedCtx.mpLastSchedulerData = pSchedulerData; + rSchedCtx.mpFirstSchedulerData[nTaskPriority] = pSchedulerData; + rSchedCtx.mpLastSchedulerData[nTaskPriority] = pSchedulerData; } else { - rSchedCtx.mpLastSchedulerData->mpNext = pSchedulerData; - rSchedCtx.mpLastSchedulerData = pSchedulerData; + rSchedCtx.mpLastSchedulerData[nTaskPriority]->mpNext = pSchedulerData; + rSchedCtx.mpLastSchedulerData[nTaskPriority] = pSchedulerData; } pSchedulerData->mpNext = nullptr; } static inline ImplSchedulerData* DropSchedulerData( ImplSchedulerContext &rSchedCtx, ImplSchedulerData * const pPrevSchedulerData, - const ImplSchedulerData * const pSchedulerData ) + const ImplSchedulerData * const pSchedulerData, const int nTaskPriority) { assert( pSchedulerData ); if ( pPrevSchedulerData ) assert( pPrevSchedulerData->mpNext == pSchedulerData ); else - assert( rSchedCtx.mpFirstSchedulerData == pSchedulerData ); + assert(rSchedCtx.mpFirstSchedulerData[nTaskPriority] == pSchedulerData); ImplSchedulerData * const pSchedulerDataNext = pSchedulerData->mpNext; if ( pPrevSchedulerData ) pPrevSchedulerData->mpNext = pSchedulerDataNext; else - rSchedCtx.mpFirstSchedulerData = pSchedulerDataNext; + rSchedCtx.mpFirstSchedulerData[nTaskPriority] = pSchedulerDataNext; if ( !pSchedulerDataNext ) - rSchedCtx.mpLastSchedulerData = pPrevSchedulerData; + rSchedCtx.mpLastSchedulerData[nTaskPriority] = pPrevSchedulerData; return pSchedulerDataNext; } @@ -348,10 +364,9 @@ bool Scheduler::ProcessTaskScheduling() // Allow for decimals, so subtract in the compare (needed at least on iOS) if ( nTime < rSchedCtx.mnTimerStart + rSchedCtx.mnTimerPeriod -1) { - SAL_WARN( "vcl.schedule", "we're too early - restart the timer!" ); - UpdateSystemTimer( rSchedCtx, - rSchedCtx.mnTimerStart + rSchedCtx.mnTimerPeriod - nTime, - true, nTime ); + int nSleep = rSchedCtx.mnTimerStart + rSchedCtx.mnTimerPeriod - nTime; + SAL_WARN("vcl.schedule", "we're too early - restart the timer (" << nSleep << "ms)!"); + UpdateSystemTimer(rSchedCtx, nSleep, true, nTime); return false; } @@ -359,69 +374,72 @@ bool Scheduler::ProcessTaskScheduling() ImplSchedulerData* pPrevSchedulerData = nullptr; ImplSchedulerData *pMostUrgent = nullptr; ImplSchedulerData *pPrevMostUrgent = nullptr; + int nMostUrgentPriority = 0; sal_uInt64 nMinPeriod = InfiniteTimeoutMs; - sal_uInt64 nMostUrgentPeriod = InfiniteTimeoutMs; sal_uInt64 nReadyPeriod = InfiniteTimeoutMs; unsigned nTasks = 0; + int nTaskPriority = 0; - pSchedulerData = rSchedCtx.mpFirstSchedulerData; - while ( pSchedulerData ) + for (; nTaskPriority < PRIO_COUNT; ++nTaskPriority) { - ++nTasks; - const Timer *timer = dynamic_cast<Timer*>( pSchedulerData->mpTask ); - if ( timer ) - SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() << " " - << pSchedulerData << " " << *pSchedulerData << " " << *timer ); - else if ( pSchedulerData->mpTask ) - SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() << " " - << pSchedulerData << " " << *pSchedulerData - << " " << *pSchedulerData->mpTask ); - else - SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() << " " - << pSchedulerData << " " << *pSchedulerData << " (to be deleted)" ); - - // Should the Task be released from scheduling or stacked? - if ( !pSchedulerData->mpTask || !pSchedulerData->mpTask->IsActive() - || pSchedulerData->mbInScheduler ) + pSchedulerData = rSchedCtx.mpFirstSchedulerData[nTaskPriority]; + pPrevSchedulerData = nullptr; + while (pSchedulerData) { - ImplSchedulerData * const pSchedulerDataNext = - DropSchedulerData( rSchedCtx, pPrevSchedulerData, pSchedulerData ); - if ( pSchedulerData->mbInScheduler ) - { - pSchedulerData->mpNext = rSchedCtx.mpSchedulerStack; - rSchedCtx.mpSchedulerStack = pSchedulerData; - } + ++nTasks; + const Timer *timer = dynamic_cast<Timer*>( pSchedulerData->mpTask ); + if ( timer ) + SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() << " " + << pSchedulerData << " " << *pSchedulerData << " " << *timer ); + else if ( pSchedulerData->mpTask ) + SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() << " " + << pSchedulerData << " " << *pSchedulerData + << " " << *pSchedulerData->mpTask ); else + SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() << " " + << pSchedulerData << " " << *pSchedulerData << " (to be deleted)" ); + + // Should the Task be released from scheduling? + assert(!pSchedulerData->mbInScheduler); + if (!pSchedulerData->mpTask || !pSchedulerData->mpTask->IsActive()) { + ImplSchedulerData * const pSchedulerDataNext = + DropSchedulerData(rSchedCtx, pPrevSchedulerData, pSchedulerData, nTaskPriority); if ( pSchedulerData->mpTask ) pSchedulerData->mpTask->mpSchedulerData = nullptr; delete pSchedulerData; + pSchedulerData = pSchedulerDataNext; + continue; } - pSchedulerData = pSchedulerDataNext; - continue; - } - assert( pSchedulerData->mpTask ); - if ( !pSchedulerData->mpTask->IsActive() ) - goto next_entry; + assert(pSchedulerData->mpTask); + if (pSchedulerData->mpTask->IsActive()) + { + nReadyPeriod = pSchedulerData->mpTask->UpdateMinPeriod( nMinPeriod, nTime ); + if (ImmediateTimeoutMs == nReadyPeriod) + { + if (!pMostUrgent) + { + pPrevMostUrgent = pPrevSchedulerData; + pMostUrgent = pSchedulerData; + nMostUrgentPriority = nTaskPriority; + } + else + { + nMinPeriod = ImmediateTimeoutMs; + break; + } + } + else if (nMinPeriod > nReadyPeriod) + nMinPeriod = nReadyPeriod; + } - // skip ready tasks with lower priority than the most urgent (numerical lower is higher) - nReadyPeriod = pSchedulerData->mpTask->UpdateMinPeriod( nMinPeriod, nTime ); - if ( ImmediateTimeoutMs == nReadyPeriod && - (!pMostUrgent || (pSchedulerData->mpTask->GetPriority() < pMostUrgent->mpTask->GetPriority())) ) - { - if ( pMostUrgent && nMinPeriod > nMostUrgentPeriod ) - nMinPeriod = nMostUrgentPeriod; - pPrevMostUrgent = pPrevSchedulerData; - pMostUrgent = pSchedulerData; - nMostUrgentPeriod = nReadyPeriod; + pPrevSchedulerData = pSchedulerData; + pSchedulerData = pSchedulerData->mpNext; } - else if ( nMinPeriod > nReadyPeriod ) - nMinPeriod = nReadyPeriod; -next_entry: - pPrevSchedulerData = pSchedulerData; - pSchedulerData = pSchedulerData->mpNext; + if (ImmediateTimeoutMs == nMinPeriod) + break; } if ( InfiniteTimeoutMs != nMinPeriod ) @@ -441,10 +459,15 @@ next_entry: // prepare Scheduler object for deletion after handling pTask->SetDeletionFlags(); - // invoke the task - // defer pushing the scheduler stack to next run, as most tasks will - // not run a nested Scheduler loop and don't need a stack push! pMostUrgent->mbInScheduler = true; + + // always push the stack, as we don't traverse the whole list to push later + DropSchedulerData(rSchedCtx, pPrevMostUrgent, pMostUrgent, nMostUrgentPriority); + pMostUrgent->mpNext = rSchedCtx.mpSchedulerStack; + rSchedCtx.mpSchedulerStack = pMostUrgent; + rSchedCtx.mpSchedulerStackTop = pMostUrgent; + + // invoke the task sal_uInt32 nLockCount = Unlock( true ); try { @@ -472,34 +495,35 @@ next_entry: SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() << " " << pMostUrgent << " invoke-out" ); - // eventually pop the scheduler stack + // pop the scheduler stack + pSchedulerData = rSchedCtx.mpSchedulerStack; + assert(pSchedulerData == pMostUrgent); + rSchedCtx.mpSchedulerStack = pSchedulerData->mpNext; + + const bool bTaskAlive = pMostUrgent->mpTask && pMostUrgent->mpTask->IsActive(); + if (!bTaskAlive) + { + if (pMostUrgent->mpTask) + pMostUrgent->mpTask->mpSchedulerData = nullptr; + delete pMostUrgent; + } + else + AppendSchedulerData(rSchedCtx, pMostUrgent); + // this just happens for nested calls, which renders all accounting // invalid, so we just enforce a rescheduling! - if ( pMostUrgent == pSVData->maSchedCtx.mpSchedulerStack ) + if (rSchedCtx.mpSchedulerStackTop != pSchedulerData) { - pSchedulerData = pSVData->maSchedCtx.mpSchedulerStack; - pSVData->maSchedCtx.mpSchedulerStack = pSchedulerData->mpNext; - AppendSchedulerData( rSchedCtx, pSchedulerData ); UpdateSystemTimer( rSchedCtx, ImmediateTimeoutMs, true, tools::Time::GetSystemTicks() ); } - else + else if (bTaskAlive) { - // 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->mpTask->IsActive() ) - { - pMostUrgent->mnUpdateTime = nTime; - nReadyPeriod = pMostUrgent->mpTask->UpdateMinPeriod( nMinPeriod, nTime ); - if ( nMinPeriod > nReadyPeriod ) - nMinPeriod = nReadyPeriod; - UpdateSystemTimer( rSchedCtx, nMinPeriod, false, nTime ); - } + pMostUrgent->mnUpdateTime = nTime; + nReadyPeriod = pMostUrgent->mpTask->UpdateMinPeriod( nMinPeriod, nTime ); + if ( nMinPeriod > nReadyPeriod ) + nMinPeriod = nReadyPeriod; + UpdateSystemTimer( rSchedCtx, nMinPeriod, false, nTime ); } } diff --git a/vcl/source/app/svapp.cxx b/vcl/source/app/svapp.cxx index ec151895815a..ca3848d58a22 100644 --- a/vcl/source/app/svapp.cxx +++ b/vcl/source/app/svapp.cxx @@ -503,21 +503,24 @@ void Scheduler::ProcessEventsToIdle() // events were processed at some point, but our check can't prevent further // processing in the main thread, which may add new events, so skip it. const ImplSVData* pSVData = ImplGetSVData(); - if ( !pSVData->mpDefInst->IsMainThread() ) + if (!pSVData->mpDefInst->IsMainThread()) return; - const ImplSchedulerData* pSchedulerData = pSVData->maSchedCtx.mpFirstSchedulerData; - while ( pSchedulerData ) + for (int nTaskPriority = 0; nTaskPriority < PRIO_COUNT; ++nTaskPriority) { - if ( pSchedulerData->mpTask && !pSchedulerData->mbInScheduler ) + const ImplSchedulerData* pSchedulerData = pSVData->maSchedCtx.mpFirstSchedulerData[nTaskPriority]; + while (pSchedulerData) { - Idle *pIdle = dynamic_cast<Idle*>( pSchedulerData->mpTask ); - if ( pIdle && pIdle->IsActive() ) + if (pSchedulerData->mpTask && !pSchedulerData->mbInScheduler) { - SAL_WARN( "vcl.schedule", "Unprocessed Idle: " - << pIdle << " " << pIdle->GetDebugName() ); + Idle *pIdle = dynamic_cast<Idle*>(pSchedulerData->mpTask); + if (pIdle && pIdle->IsActive()) + { + SAL_WARN("vcl.schedule", "Unprocessed Idle: " + << pIdle << " " << pIdle->GetDebugName()); + } } + pSchedulerData = pSchedulerData->mpNext; } - pSchedulerData = pSchedulerData->mpNext; } #endif } |