Assert active Tasks on scheduler de-init

This shuts down the VCL scheduler later in the DeInitVCL call and
asserts active pending tasks, as these will never be processed
after shutdown.

There is currently a TODO whitelist, so the unit tests still pass
but probably most of these should be fixed. No task in the list
looks critical - all seem to do some idle / cleanup work.

This also processes all the Idles before shutdown. All seem to be
of type sfx::SfxItemDisruptor_Impl.

Change-Id: I9cc484a525cc2bacd54c4f271f86997517393e92
Reviewed-on: https://gerrit.libreoffice.org/40533
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox
index 46a75f5a..8539f47 100644
--- a/include/sal/log-areas.dox
+++ b/include/sal/log-areas.dox
@@ -459,6 +459,7 @@ certain functionality.
@li @c vcl.plugadapt - the Unix/X11 backend plugin mechanism
@li @c vcl.quartz
@li @c vcl.schedule - scheduler / main-loop information
@li @c vcl.schedule.deinit
@li @c vcl.screensaverinhibitor
@li @c vcl.scrollbar - Scroll Bars
@li @c vcl.se - VCL Session Manager
diff --git a/vcl/osx/salinst.cxx b/vcl/osx/salinst.cxx
index 5e5415f..56dd7be 100644
--- a/vcl/osx/salinst.cxx
+++ b/vcl/osx/salinst.cxx
@@ -108,6 +108,7 @@ void AquaSalInstance::delayedSettingsChanged( bool bInvalidate )
{
    osl::Guard< comphelper::SolarMutex > aGuard( *mpSalYieldMutex );
    AquaDelayedSettingsChanged* pIdle = new AquaDelayedSettingsChanged( bInvalidate );
    pIdle->SetDebugName( "AquaSalInstance AquaDelayedSettingsChanged" );
    pIdle->Start();
}

diff --git a/vcl/source/app/scheduler.cxx b/vcl/source/app/scheduler.cxx
index 54922cc..d4d1638 100644
--- a/vcl/source/app/scheduler.cxx
+++ b/vcl/source/app/scheduler.cxx
@@ -95,6 +95,22 @@ void Scheduler::ImplDeInitScheduler()
    DBG_TESTSOLARMUTEX();

    SchedulerGuard aSchedulerGuard;

#if OSL_DEBUG_LEVEL > 0
    {
        ImplSchedulerData* pSchedulerData = rSchedCtx.mpFirstSchedulerData;
        sal_uInt32 nTasks = 0;
        while ( pSchedulerData )
        {
            ++nTasks;
            pSchedulerData = pSchedulerData->mpNext;
        }
        SAL_INFO( "vcl.schedule.deinit", "DeInit the scheduler - tasks: " << nTasks );
    }

    // clean up all the sfx::SfxItemDisruptor_Impl Idles
    ProcessEventsToIdle();
#endif
    rSchedCtx.mbActive = false;

    assert( nullptr == rSchedCtx.mpSchedulerStack );
@@ -103,19 +119,57 @@ void Scheduler::ImplDeInitScheduler()
    if (rSchedCtx.mpSalTimer) rSchedCtx.mpSalTimer->Stop();
    DELETEZ( rSchedCtx.mpSalTimer );

#if OSL_DEBUG_LEVEL > 0
    sal_uInt32 nActiveTasks = 0, nIgnoredTasks = 0;
#endif
    ImplSchedulerData* pSchedulerData = rSchedCtx.mpFirstSchedulerData;
    while ( pSchedulerData )
    {
        Task *pTask = pSchedulerData->mpTask;
        if ( pTask )
        {
            pTask->mbActive = false;
            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(), "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::win GdiPlusBuffer aGdiPlusBuffer" )
                        ))
                {
                    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;
        }
        ImplSchedulerData* pDeleteSchedulerData = pSchedulerData;
        pSchedulerData = pSchedulerData->mpNext;
        delete pDeleteSchedulerData;
    }
#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

    rSchedCtx.mpFirstSchedulerData = nullptr;
    rSchedCtx.mpLastSchedulerData  = nullptr;
diff --git a/vcl/source/app/svmain.cxx b/vcl/source/app/svmain.cxx
index a8e42ad..581fb82 100644
--- a/vcl/source/app/svmain.cxx
+++ b/vcl/source/app/svmain.cxx
@@ -469,8 +469,6 @@ void DeInitVCL()
        pSVData->mpSettingsConfigItem = nullptr;
    }

    Scheduler::ImplDeInitScheduler();

    pSVData->maWinData.maMsgBoxImgList.clear();
    pSVData->maCtrlData.maCheckImgList.clear();
    pSVData->maCtrlData.maRadioImgList.clear();
@@ -578,6 +576,8 @@ void DeInitVCL()
    delete pSVData->maGDIData.mpScreenFontCache;
    pSVData->maGDIData.mpScreenFontCache = nullptr;

    Scheduler::ImplDeInitScheduler();

    // destroy all Sal interfaces before destroying the instance
    // and thereby unloading the plugin
    delete pSVData->mpSalSystem;
@@ -611,6 +611,7 @@ void DeInitVCL()
    pSVData->maWinData.mpTrackWin = nullptr;
    pSVData->maWinData.mpAutoScrollWin = nullptr;
    pSVData->maWinData.mpLastWheelWindow = nullptr;

    // Deinit Sal
    if (pSVData->mpDefInst)
    {