diff options
Diffstat (limited to '')
-rw-r--r-- | vcl/source/app/scheduler.cxx | 664 |
1 files changed, 664 insertions, 0 deletions
diff --git a/vcl/source/app/scheduler.cxx b/vcl/source/app/scheduler.cxx new file mode 100644 index 000000000..927de71c4 --- /dev/null +++ b/vcl/source/app/scheduler.cxx @@ -0,0 +1,664 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> +#include <cstdlib> +#include <exception> +#include <typeinfo> + +#include <com/sun/star/uno/Exception.hpp> +#include <sal/log.hxx> +#include <sal/types.h> +#include <svdata.hxx> +#include <tools/time.hxx> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <unotools/configmgr.hxx> +#include <vcl/TaskStopwatch.hxx> +#include <vcl/scheduler.hxx> +#include <vcl/idle.hxx> +#include <saltimer.hxx> +#include <salinst.hxx> +#include <comphelper/profilezone.hxx> +#include <schedulerimpl.hxx> + +namespace { + +template< typename charT, typename traits > +std::basic_ostream<charT, traits> & operator <<( + std::basic_ostream<charT, traits> & stream, const Task& task ) +{ + stream << "a: " << task.IsActive() << " p: " << static_cast<int>(task.GetPriority()); + const char *name = task.GetDebugName(); + if( nullptr == name ) + return stream << " (nullptr)"; + else + return stream << " " << name; +} + +/** + * clang won't compile this in the Timer.hxx header, even with a class Idle + * forward definition, due to the incomplete Idle type in the template. + * Currently the code is just used in the Scheduler, so we keep it local. + * + * @see http://clang.llvm.org/compatibility.html#undep_incomplete + */ +template< typename charT, typename traits > +std::basic_ostream<charT, traits> & operator <<( + std::basic_ostream<charT, traits> & stream, const Timer& timer ) +{ + bool bIsIdle = (dynamic_cast<const Idle*>( &timer ) != nullptr); + stream << (bIsIdle ? "Idle " : "Timer") + << " a: " << timer.IsActive() << " p: " << static_cast<int>(timer.GetPriority()); + const char *name = timer.GetDebugName(); + if ( nullptr == name ) + stream << " (nullptr)"; + else + stream << " " << name; + if ( !bIsIdle ) + stream << " " << timer.GetTimeout() << "ms"; + stream << " (" << &timer << ")"; + return stream; +} + +template< typename charT, typename traits > +std::basic_ostream<charT, traits> & operator <<( + std::basic_ostream<charT, traits> & stream, const Idle& idle ) +{ + return stream << static_cast<const Timer*>( &idle ); +} + +template< typename charT, typename traits > +std::basic_ostream<charT, traits> & operator <<( + std::basic_ostream<charT, traits> & stream, const ImplSchedulerData& data ) +{ + stream << " i: " << data.mbInScheduler; + return stream; +} + +} // end anonymous namespace + +unsigned int TaskStopwatch::m_nTimeSlice = TaskStopwatch::nDefaultTimeSlice; + +void Scheduler::ImplDeInitScheduler() +{ + ImplSVData* pSVData = ImplGetSVData(); + assert( pSVData != nullptr ); + ImplSchedulerContext &rSchedCtx = pSVData->maSchedCtx; + + DBG_TESTSOLARMUTEX(); + + 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[nTaskPriority]; + while ( pSchedulerData ) + { + ++nTasks; + pSchedulerData = pSchedulerData->mpNext; + } + } + SAL_INFO( "vcl.schedule.deinit", + "DeInit the scheduler - pending tasks: " << nTasks ); + + // clean up all the sfx::SfxItemDisruptor_Impl Idles + Unlock(); + ProcessEventsToIdle(); + Lock(); +#endif + rSchedCtx.mbActive = false; + + assert( nullptr == rSchedCtx.mpSchedulerStack ); + + if (rSchedCtx.mpSalTimer) rSchedCtx.mpSalTimer->Stop(); + delete rSchedCtx.mpSalTimer; + rSchedCtx.mpSalTimer = nullptr; + +#if OSL_DEBUG_LEVEL > 0 + sal_uInt32 nActiveTasks = 0, nIgnoredTasks = 0; +#endif + nTaskPriority = 0; + ImplSchedulerData* pSchedulerData = nullptr; + +next_priority: + pSchedulerData = rSchedCtx.mpFirstSchedulerData[nTaskPriority]; + while ( pSchedulerData ) + { + Task *pTask = pSchedulerData->mpTask; + if ( pTask ) + { + if ( pTask->mbActive ) + { +#if OSL_DEBUG_LEVEL > 0 + const char *sIgnored = ""; + ++nActiveTasks; + // TODO: shutdown these timers before Scheduler de-init + // TODO: remove Task from static object + if ( pTask->GetDebugName() && ( false + || !strcmp( pTask->GetDebugName(), "desktop::Desktop m_firstRunTimer" ) + || !strcmp( pTask->GetDebugName(), "DrawWorkStartupTimer" ) + || !strcmp( pTask->GetDebugName(), "editeng::ImpEditEngine aOnlineSpellTimer" ) + || !strcmp( pTask->GetDebugName(), "ImplHandleMouseMsg SalData::mpMouseLeaveTimer" ) + || !strcmp( pTask->GetDebugName(), "sc ScModule IdleTimer" ) + || !strcmp( pTask->GetDebugName(), "sd::CacheConfiguration maReleaseTimer" ) + || !strcmp( pTask->GetDebugName(), "svtools::GraphicCache maReleaseTimer" ) + || !strcmp( pTask->GetDebugName(), "svtools::GraphicObject mpSwapOutTimer" ) + || !strcmp( pTask->GetDebugName(), "svx OLEObjCache pTimer UnloadCheck" ) + || !strcmp( pTask->GetDebugName(), "vcl SystemDependentDataBuffer aSystemDependentDataBuffer" ) + )) + { + sIgnored = " (ignored)"; + ++nIgnoredTasks; + } + const Timer *timer = dynamic_cast<Timer*>( pTask ); + if ( timer ) + SAL_WARN( "vcl.schedule.deinit", "DeInit task: " << *timer << sIgnored ); + else + SAL_WARN( "vcl.schedule.deinit", "DeInit task: " << *pTask << sIgnored ); +#endif + pTask->mbActive = false; + } + pTask->mpSchedulerData = nullptr; + pTask->SetStatic(); + } + ImplSchedulerData* pDeleteSchedulerData = pSchedulerData; + 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: " + << nActiveTasks << " (ignored: " << nIgnoredTasks << ")" ); +// assert( nIgnoredTasks == nActiveTasks ); +#endif + + for (nTaskPriority = 0; nTaskPriority < PRIO_COUNT; ++nTaskPriority) + { + rSchedCtx.mpFirstSchedulerData[nTaskPriority] = nullptr; + rSchedCtx.mpLastSchedulerData[nTaskPriority] = nullptr; + } + rSchedCtx.mnTimerPeriod = InfiniteTimeoutMs; +} + +void Scheduler::Lock() +{ + ImplSVData* pSVData = ImplGetSVData(); + assert( pSVData != nullptr ); + pSVData->maSchedCtx.maMutex.lock(); +} + +void Scheduler::Unlock() +{ + ImplSVData* pSVData = ImplGetSVData(); + assert( pSVData != nullptr ); + pSVData->maSchedCtx.maMutex.unlock(); +} + +/** + * Start a new timer if we need to for nMS duration. + * + * if this is longer than the existing duration we're + * waiting for, do nothing - unless bForce - which means + * to reset the minimum period; used by the scheduled itself. + */ +void Scheduler::ImplStartTimer(sal_uInt64 nMS, bool bForce, sal_uInt64 nTime) +{ + ImplSVData* pSVData = ImplGetSVData(); + ImplSchedulerContext &rSchedCtx = pSVData->maSchedCtx; + if ( !rSchedCtx.mbActive ) + return; + + if (!rSchedCtx.mpSalTimer) + { + rSchedCtx.mnTimerStart = 0; + rSchedCtx.mnTimerPeriod = InfiniteTimeoutMs; + rSchedCtx.mpSalTimer = pSVData->mpDefInst->CreateSalTimer(); + rSchedCtx.mpSalTimer->SetCallback(Scheduler::CallbackTaskScheduling); + } + + assert(SAL_MAX_UINT64 - nMS >= nTime); + + sal_uInt64 nProposedTimeout = nTime + nMS; + sal_uInt64 nCurTimeout = ( rSchedCtx.mnTimerPeriod == InfiniteTimeoutMs ) + ? SAL_MAX_UINT64 : rSchedCtx.mnTimerStart + rSchedCtx.mnTimerPeriod; + + // Only if smaller timeout, to avoid skipping. + // Force instant wakeup on 0ms, if the previous period was not 0ms + if (bForce || nProposedTimeout < nCurTimeout || (!nMS && rSchedCtx.mnTimerPeriod)) + { + SAL_INFO( "vcl.schedule", " Starting scheduler system timer (" << nMS << "ms)" ); + rSchedCtx.mnTimerStart = nTime; + rSchedCtx.mnTimerPeriod = nMS; + rSchedCtx.mpSalTimer->Start( nMS ); + } +} + +static bool g_bDeterministicMode = false; + +void Scheduler::SetDeterministicMode(bool bDeterministic) +{ + g_bDeterministicMode = bDeterministic; +} + +bool Scheduler::GetDeterministicMode() +{ + return g_bDeterministicMode; +} + +inline void Scheduler::UpdateSystemTimer( ImplSchedulerContext &rSchedCtx, + const sal_uInt64 nMinPeriod, + const bool bForce, const sal_uInt64 nTime ) +{ + if ( InfiniteTimeoutMs == nMinPeriod ) + { + SAL_INFO("vcl.schedule", " Stopping system timer"); + if ( rSchedCtx.mpSalTimer ) + rSchedCtx.mpSalTimer->Stop(); + rSchedCtx.mnTimerPeriod = nMinPeriod; + } + else + Scheduler::ImplStartTimer( nMinPeriod, bForce, nTime ); +} + +static void AppendSchedulerData( ImplSchedulerContext &rSchedCtx, + ImplSchedulerData * const pSchedulerData) +{ + assert(pSchedulerData->mpTask); + pSchedulerData->mePriority = pSchedulerData->mpTask->GetPriority(); + pSchedulerData->mpNext = nullptr; + + const int nTaskPriority = static_cast<int>(pSchedulerData->mePriority); + if (!rSchedCtx.mpLastSchedulerData[nTaskPriority]) + { + rSchedCtx.mpFirstSchedulerData[nTaskPriority] = pSchedulerData; + rSchedCtx.mpLastSchedulerData[nTaskPriority] = pSchedulerData; + } + else + { + rSchedCtx.mpLastSchedulerData[nTaskPriority]->mpNext = pSchedulerData; + rSchedCtx.mpLastSchedulerData[nTaskPriority] = pSchedulerData; + } +} + +static ImplSchedulerData* DropSchedulerData( + ImplSchedulerContext &rSchedCtx, ImplSchedulerData * const pPrevSchedulerData, + const ImplSchedulerData * const pSchedulerData, const int nTaskPriority) +{ + assert( pSchedulerData ); + if ( pPrevSchedulerData ) + assert( pPrevSchedulerData->mpNext == pSchedulerData ); + else + assert(rSchedCtx.mpFirstSchedulerData[nTaskPriority] == pSchedulerData); + + ImplSchedulerData * const pSchedulerDataNext = pSchedulerData->mpNext; + if ( pPrevSchedulerData ) + pPrevSchedulerData->mpNext = pSchedulerDataNext; + else + rSchedCtx.mpFirstSchedulerData[nTaskPriority] = pSchedulerDataNext; + if ( !pSchedulerDataNext ) + rSchedCtx.mpLastSchedulerData[nTaskPriority] = pPrevSchedulerData; + return pSchedulerDataNext; +} + +void Scheduler::CallbackTaskScheduling() +{ + ImplSVData *pSVData = ImplGetSVData(); + ImplSchedulerContext &rSchedCtx = pSVData->maSchedCtx; + + DBG_TESTSOLARMUTEX(); + + SchedulerGuard aSchedulerGuard; + if ( !rSchedCtx.mbActive || InfiniteTimeoutMs == rSchedCtx.mnTimerPeriod ) + return; + + sal_uInt64 nTime = tools::Time::GetSystemTicks(); + // Allow for decimals, so subtract in the compare (needed at least on iOS) + if ( nTime < rSchedCtx.mnTimerStart + rSchedCtx.mnTimerPeriod -1) + { + int nSleep = rSchedCtx.mnTimerStart + rSchedCtx.mnTimerPeriod - nTime; + UpdateSystemTimer(rSchedCtx, nSleep, true, nTime); + return; + } + + ImplSchedulerData* pSchedulerData = nullptr; + ImplSchedulerData* pPrevSchedulerData = nullptr; + ImplSchedulerData *pMostUrgent = nullptr; + ImplSchedulerData *pPrevMostUrgent = nullptr; + int nMostUrgentPriority = 0; + sal_uInt64 nMinPeriod = InfiniteTimeoutMs; + sal_uInt64 nReadyPeriod = InfiniteTimeoutMs; + unsigned nTasks = 0; + int nTaskPriority = 0; + + for (; nTaskPriority < PRIO_COUNT; ++nTaskPriority) + { + pSchedulerData = rSchedCtx.mpFirstSchedulerData[nTaskPriority]; + pPrevSchedulerData = nullptr; + while (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; + } + + assert(pSchedulerData->mpTask); + if (pSchedulerData->mpTask->IsActive()) + { + nReadyPeriod = pSchedulerData->mpTask->UpdateMinPeriod( nTime ); + if (ImmediateTimeoutMs == nReadyPeriod) + { + if (!pMostUrgent) + { + pPrevMostUrgent = pPrevSchedulerData; + pMostUrgent = pSchedulerData; + nMostUrgentPriority = nTaskPriority; + } + else + { + nMinPeriod = ImmediateTimeoutMs; + break; + } + } + else if (nMinPeriod > nReadyPeriod) + nMinPeriod = nReadyPeriod; + } + + pPrevSchedulerData = pSchedulerData; + pSchedulerData = pSchedulerData->mpNext; + } + + if (ImmediateTimeoutMs == nMinPeriod) + break; + } + +// tdf#148435 Apparently calling AnyInput on Mac and filtering for just input, gives +// you window creation / re-sizing events which then trigger idle paint +// events which then deadlock if called with the scheduler lock. +// So since this is an optimisation, just don't do this on mac. +#ifndef MACOSX + // Delay invoking tasks with idle priorities as long as there are user input or repaint events + // in the OS event queue. This will often effectively compress such events and repaint only + // once at the end, improving performance in cases such as repeated zooming with a complex document. + if ( pMostUrgent && pMostUrgent->mePriority >= TaskPriority::HIGH_IDLE + && Application::AnyInput( VclInputFlags::MOUSE | VclInputFlags::KEYBOARD | VclInputFlags::PAINT )) + { + SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() + << " idle priority task " << pMostUrgent << " delayed, system events pending" ); + pMostUrgent = nullptr; + nMinPeriod = 0; + } +#endif + + if (InfiniteTimeoutMs != nMinPeriod) + SAL_INFO("vcl.schedule", + "Calculated minimum timeout as " << nMinPeriod << " of " << nTasks << " tasks"); + UpdateSystemTimer(rSchedCtx, nMinPeriod, true, nTime); + + if ( !pMostUrgent ) + return; + + SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() << " " + << pMostUrgent << " invoke-in " << *pMostUrgent->mpTask ); + + Task *pTask = pMostUrgent->mpTask; + + comphelper::ProfileZone aZone( pTask->GetDebugName() ); + + // prepare Scheduler object for deletion after handling + pTask->SetDeletionFlags(); + + assert(!pMostUrgent->mbInScheduler); + 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 + Unlock(); + /* + * Current policy is that scheduler tasks aren't allowed to throw an exception. + * Because otherwise the exception is caught somewhere totally unrelated. + * TODO Ideally we could capture a proper backtrace and feed this into breakpad, + * which is do-able, but requires writing some assembly. + * See also SalUserEventList::DispatchUserEvents + */ + try + { + pTask->Invoke(); + } + catch (css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION("vcl.schedule", "Uncaught"); + std::abort(); + } + catch (std::exception& e) + { + SAL_WARN("vcl.schedule", "Uncaught " << typeid(e).name() << " " << e.what()); + std::abort(); + } + catch (...) + { + SAL_WARN("vcl.schedule", "Uncaught exception during Task::Invoke()!"); + std::abort(); + } + Lock(); + + assert(pMostUrgent->mbInScheduler); + pMostUrgent->mbInScheduler = false; + + SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() << " " + << pMostUrgent << " invoke-out" ); + + // pop the scheduler stack + pSchedulerData = rSchedCtx.mpSchedulerStack; + assert(pSchedulerData == pMostUrgent); + rSchedCtx.mpSchedulerStack = pSchedulerData->mpNext; + + // coverity[check_after_deref : FALSE] - pMostUrgent->mpTask is initially pMostUrgent->mpTask, but Task::Invoke can clear it + 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 (rSchedCtx.mpSchedulerStackTop != pSchedulerData) + { + UpdateSystemTimer( rSchedCtx, ImmediateTimeoutMs, true, + tools::Time::GetSystemTicks() ); + } + else if (bTaskAlive) + { + pMostUrgent->mnUpdateTime = nTime; + nReadyPeriod = pMostUrgent->mpTask->UpdateMinPeriod( nTime ); + if ( nMinPeriod > nReadyPeriod ) + nMinPeriod = nReadyPeriod; + UpdateSystemTimer( rSchedCtx, nMinPeriod, false, nTime ); + } +} + +void Scheduler::Wakeup() +{ + Scheduler::ImplStartTimer( 0, false, tools::Time::GetSystemTicks() ); +} + +void Task::StartTimer( sal_uInt64 nMS ) +{ + Scheduler::ImplStartTimer( nMS, false, tools::Time::GetSystemTicks() ); +} + +void Task::SetDeletionFlags() +{ + mbActive = false; +} + +void Task::Start(const bool bStartTimer) +{ + ImplSVData *const pSVData = ImplGetSVData(); + ImplSchedulerContext &rSchedCtx = pSVData->maSchedCtx; + + SchedulerGuard aSchedulerGuard; + if ( !rSchedCtx.mbActive ) + return; + + // is the task scheduled in the correct priority queue? + // if not we have to get a new data object, as we don't want to traverse + // the whole list to move the data to the correct list, as the task list + // is just single linked. + // Task priority doesn't change that often AFAIK, or we might need to + // start caching ImplSchedulerData objects. + if (mpSchedulerData && mpSchedulerData->mePriority != mePriority) + { + mpSchedulerData->mpTask = nullptr; + mpSchedulerData = nullptr; + } + mbActive = true; + + if ( !mpSchedulerData ) + { + // insert Task + ImplSchedulerData* pSchedulerData = new ImplSchedulerData; + pSchedulerData->mpTask = this; + pSchedulerData->mbInScheduler = false; + // mePriority is set in AppendSchedulerData + mpSchedulerData = pSchedulerData; + + AppendSchedulerData( rSchedCtx, pSchedulerData ); + SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() + << " " << mpSchedulerData << " added " << *this ); + } + else + SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() + << " " << mpSchedulerData << " restarted " << *this ); + + mpSchedulerData->mnUpdateTime = tools::Time::GetSystemTicks(); + + if (bStartTimer) + Task::StartTimer(0); +} + +void Task::Stop() +{ + SAL_INFO_IF( mbActive, "vcl.schedule", tools::Time::GetSystemTicks() + << " " << mpSchedulerData << " stopped " << *this ); + mbActive = false; +} + +void Task::SetPriority(TaskPriority ePriority) +{ + // you don't actually need to call Stop() before but Start() after, but we + // can't check that and don't know when Start() should be called. + SAL_WARN_IF(mpSchedulerData && mbActive, "vcl.schedule", + "Stop the task before changing the priority, as it will just " + "change after the task was scheduled with the old prio!"); + mePriority = ePriority; +} + +Task& Task::operator=( const Task& rTask ) +{ + if(this == &rTask) + return *this; + + if ( IsActive() ) + Stop(); + + mbActive = false; + mePriority = rTask.mePriority; + + if ( rTask.IsActive() ) + Start(); + + return *this; +} + +Task::Task( const char *pDebugName ) + : mpSchedulerData( nullptr ) + , mpDebugName( pDebugName ) + , mePriority( TaskPriority::DEFAULT ) + , mbActive( false ) + , mbStatic( false ) +{ + assert(mpDebugName); +} + +Task::Task( const Task& rTask ) + : mpSchedulerData( nullptr ) + , mpDebugName( rTask.mpDebugName ) + , mePriority( rTask.mePriority ) + , mbActive( false ) + , mbStatic( false ) +{ + assert(mpDebugName); + if ( rTask.IsActive() ) + Start(); +} + +Task::~Task() COVERITY_NOEXCEPT_FALSE +{ + if ( !IsStatic() ) + { + SchedulerGuard aSchedulerGuard; + if ( mpSchedulerData ) + mpSchedulerData->mpTask = nullptr; + } + else + assert(nullptr == mpSchedulerData || utl::ConfigManager::IsFuzzing()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |