Kaydet (Commit) c0710abf authored tarafından Jan-Marek Glogowski's avatar Jan-Marek Glogowski

Add some Scheduler unit tests and logging

 1. calling Start() for invoked tasks
 2. correctly schedule by priority
 3. self-stopping AutoTimer

This also adds SAL_INFO output to Scheduler and Task to log the
scheduling processing tasks.

Change-Id: I3c8a708d1fd51c550320f8af3f9486c43c32e358
üst dc63cc32
...@@ -462,18 +462,25 @@ public: ...@@ -462,18 +462,25 @@ public:
*/ */
static void Quit(); static void Quit();
/** Attempt to reschedule in processing of current event(s) /** Attempt to process current pending event(s)
It doesn't sleep if no events are available for processing.
@param bAllEvents If set to true, then try to process all the @param bAllEvents If set to true, then try to process all the
events. If set to false, then only process the current events. If set to false, then only process the current
event. Defaults to false. event. Defaults to false.
@returns true if any event was processed.
@see Execute, Quit, Yield, EndYield, GetSolarMutex, @see Execute, Quit, Yield, EndYield, GetSolarMutex,
GetMainThreadIdentifier, ReleaseSolarMutex, AcquireSolarMutex, GetMainThreadIdentifier, ReleaseSolarMutex, AcquireSolarMutex,
*/ */
static void Reschedule( bool bAllEvents = false ); static bool Reschedule( bool bAllEvents = false );
/** Process the next event.
/** Allow processing of the next event. It sleeps if no event is available for processing and just returns
if an event was processed.
@see Execute, Quit, Reschedule, EndYield, GetSolarMutex, @see Execute, Quit, Reschedule, EndYield, GetSolarMutex,
GetMainThreadIdentifier, ReleaseSolarMutex, AcquireSolarMutex, GetMainThreadIdentifier, ReleaseSolarMutex, AcquireSolarMutex,
......
...@@ -157,6 +157,7 @@ public: ...@@ -157,6 +157,7 @@ public:
// and timer // and timer
virtual SalYieldResult DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong nReleased) override; virtual SalYieldResult DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong nReleased) override;
virtual bool AnyInput( VclInputFlags nType ) override; virtual bool AnyInput( VclInputFlags nType ) override;
virtual bool IsMainThread() const override { return true; }
// may return NULL to disable session management // may return NULL to disable session management
virtual SalSession* CreateSalSession() override; virtual SalSession* CreateSalSession() override;
......
...@@ -142,7 +142,8 @@ public: ...@@ -142,7 +142,8 @@ public:
void PostUserEvent( AquaSalFrame* pFrame, SalEvent nType, void* pData ); void PostUserEvent( AquaSalFrame* pFrame, SalEvent nType, void* pData );
void delayedSettingsChanged( bool bInvalidate ); void delayedSettingsChanged( bool bInvalidate );
bool isNSAppThread() const; // Is this the NSAppThread?
virtual bool IsMainThread() const override;
void startedPrintJob() { mnActivePrintJobs++; } void startedPrintJob() { mnActivePrintJobs++; }
void endedPrintJob() { mnActivePrintJobs--; } void endedPrintJob() { mnActivePrintJobs--; }
......
...@@ -127,6 +127,7 @@ public: ...@@ -127,6 +127,7 @@ public:
virtual void AcquireYieldMutex( sal_uLong nCount ) = 0; virtual void AcquireYieldMutex( sal_uLong nCount ) = 0;
// return true, if yield mutex is owned by this thread, else false // return true, if yield mutex is owned by this thread, else false
virtual bool CheckYieldMutex() = 0; virtual bool CheckYieldMutex() = 0;
virtual bool IsMainThread() const = 0;
/** /**
* Wait for the next event (if bWait) and dispatch it, * Wait for the next event (if bWait) and dispatch it,
......
...@@ -21,10 +21,9 @@ ...@@ -21,10 +21,9 @@
#define INCLUDED_VCL_INC_SALTIMER_HXX #define INCLUDED_VCL_INC_SALTIMER_HXX
#include <sal/config.h> #include <sal/config.h>
#include <vcl/dllapi.h> #include <vcl/dllapi.h>
#include <salwtype.hxx> #include <salwtype.hxx>
#include <iostream>
/* /*
* note: there will be only a single instance of SalTimer * note: there will be only a single instance of SalTimer
...@@ -72,6 +71,15 @@ struct ImplSchedulerData ...@@ -72,6 +71,15 @@ struct ImplSchedulerData
const char *GetDebugName() const; const char *GetDebugName() const;
}; };
template< typename charT, typename traits >
inline std::basic_ostream<charT, traits> & operator <<(
std::basic_ostream<charT, traits> & stream, const ImplSchedulerData& data )
{
stream << " i: " << data.mbInScheduler
<< " d: " << data.mbDelete;
return stream;
}
#endif // INCLUDED_VCL_INC_SALTIMER_HXX #endif // INCLUDED_VCL_INC_SALTIMER_HXX
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
...@@ -210,6 +210,8 @@ public: ...@@ -210,6 +210,8 @@ public:
virtual SalYieldResult DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong nReleased) override; virtual SalYieldResult DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong nReleased) override;
virtual bool AnyInput( VclInputFlags nType ) override; virtual bool AnyInput( VclInputFlags nType ) override;
// impossible to handle correctly, as "main thread" depends on the dispatch mutex
virtual bool IsMainThread() const override { return false; }
virtual GenPspGraphics *CreatePrintGraphics() override; virtual GenPspGraphics *CreatePrintGraphics() override;
......
...@@ -76,6 +76,7 @@ public: ...@@ -76,6 +76,7 @@ public:
virtual SalYieldResult DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong nReleased) override; virtual SalYieldResult DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong nReleased) override;
virtual bool AnyInput( VclInputFlags nType ) override; virtual bool AnyInput( VclInputFlags nType ) override;
virtual bool IsMainThread() const override { return true; }
virtual OUString GetConnectionIdentifier() override; virtual OUString GetConnectionIdentifier() override;
void SetLib( SalXLib *pXLib ) { mpXLib = pXLib; } void SetLib( SalXLib *pXLib ) { mpXLib = pXLib; }
......
...@@ -62,6 +62,7 @@ public: ...@@ -62,6 +62,7 @@ public:
virtual sal_uIntPtr ReleaseYieldMutex() override; virtual sal_uIntPtr ReleaseYieldMutex() override;
virtual void AcquireYieldMutex( sal_uIntPtr nCount ) override; virtual void AcquireYieldMutex( sal_uIntPtr nCount ) override;
virtual bool CheckYieldMutex() override; virtual bool CheckYieldMutex() override;
virtual bool IsMainThread() const override;
virtual SalYieldResult DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong nReleased) override; virtual SalYieldResult DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong nReleased) override;
virtual bool AnyInput( VclInputFlags nType ) override; virtual bool AnyInput( VclInputFlags nType ) override;
......
...@@ -445,7 +445,7 @@ bool AquaSalInstance::CheckYieldMutex() ...@@ -445,7 +445,7 @@ bool AquaSalInstance::CheckYieldMutex()
return bRet; return bRet;
} }
bool AquaSalInstance::isNSAppThread() const bool AquaSalInstance::IsMainThread() const
{ {
return osl::Thread::getCurrentIdentifier() == maMainThread; return osl::Thread::getCurrentIdentifier() == maMainThread;
} }
...@@ -607,7 +607,7 @@ SalYieldResult AquaSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents ...@@ -607,7 +607,7 @@ SalYieldResult AquaSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents
// handle cocoa event queue // handle cocoa event queue
// cocoa events may be only handled in the thread the NSApp was created // cocoa events may be only handled in the thread the NSApp was created
bool bHadEvent = false; bool bHadEvent = false;
if( isNSAppThread() && mnActivePrintJobs == 0 ) if( IsMainThread() && mnActivePrintJobs == 0 )
{ {
// we need to be woken up by a cocoa-event // we need to be woken up by a cocoa-event
// if a user event should be posted by the event handling below // if a user event should be posted by the event handling below
...@@ -742,7 +742,7 @@ bool AquaSalInstance::AnyInput( VclInputFlags nType ) ...@@ -742,7 +742,7 @@ bool AquaSalInstance::AnyInput( VclInputFlags nType )
} }
} }
if (![NSThread isMainThread]) if (!IsMainThread())
return false; return false;
unsigned/*NSUInteger*/ nEventMask = 0; unsigned/*NSUInteger*/ nEventMask = 0;
...@@ -1019,7 +1019,7 @@ OUString AquaSalInstance::getOSVersion() ...@@ -1019,7 +1019,7 @@ OUString AquaSalInstance::getOSVersion()
YieldMutexReleaser::YieldMutexReleaser() : mnCount( 0 ) YieldMutexReleaser::YieldMutexReleaser() : mnCount( 0 )
{ {
SalData* pSalData = GetSalData(); SalData* pSalData = GetSalData();
if( ! pSalData->mpFirstInstance->isNSAppThread() ) if( ! pSalData->mpFirstInstance->IsMainThread() )
{ {
SalData::ensureThreadAutoreleasePool(); SalData::ensureThreadAutoreleasePool();
mnCount = pSalData->mpFirstInstance->ReleaseYieldMutex(); mnCount = pSalData->mpFirstInstance->ReleaseYieldMutex();
......
...@@ -33,7 +33,7 @@ bool AquaSalTimer::bDispatchTimer = false; ...@@ -33,7 +33,7 @@ bool AquaSalTimer::bDispatchTimer = false;
void ImplSalStartTimer( sal_uLong nMS ) void ImplSalStartTimer( sal_uLong nMS )
{ {
SalData* pSalData = GetSalData(); SalData* pSalData = GetSalData();
if( pSalData->mpFirstInstance->isNSAppThread() ) if( !pSalData->mpFirstInstance->IsMainThread() )
{ {
AquaSalTimer::bDispatchTimer = true; AquaSalTimer::bDispatchTimer = true;
NSTimeInterval aTI = double(nMS)/1000.0; NSTimeInterval aTI = double(nMS)/1000.0;
......
...@@ -64,9 +64,12 @@ public: ...@@ -64,9 +64,12 @@ public:
void testAutoTimer(); void testAutoTimer();
void testMultiAutoTimers(); void testMultiAutoTimers();
#endif #endif
void testRecursiveTimer(); void testAutoTimerStop();
void testNestedTimer();
void testSlowTimerCallback(); void testSlowTimerCallback();
void testTriggerIdleFromIdle(); void testTriggerIdleFromIdle();
void testInvokedReStart();
void testPriority();
CPPUNIT_TEST_SUITE(TimerTest); CPPUNIT_TEST_SUITE(TimerTest);
CPPUNIT_TEST(testIdle); CPPUNIT_TEST(testIdle);
...@@ -79,9 +82,12 @@ public: ...@@ -79,9 +82,12 @@ public:
CPPUNIT_TEST(testAutoTimer); CPPUNIT_TEST(testAutoTimer);
CPPUNIT_TEST(testMultiAutoTimers); CPPUNIT_TEST(testMultiAutoTimers);
#endif #endif
CPPUNIT_TEST(testRecursiveTimer); CPPUNIT_TEST(testAutoTimerStop);
CPPUNIT_TEST(testNestedTimer);
CPPUNIT_TEST(testSlowTimerCallback); CPPUNIT_TEST(testSlowTimerCallback);
CPPUNIT_TEST(testTriggerIdleFromIdle); CPPUNIT_TEST(testTriggerIdleFromIdle);
CPPUNIT_TEST(testInvokedReStart);
CPPUNIT_TEST(testPriority);
CPPUNIT_TEST_SUITE_END(); CPPUNIT_TEST_SUITE_END();
}; };
...@@ -100,7 +106,7 @@ class IdleBool : public Idle ...@@ -100,7 +106,7 @@ class IdleBool : public Idle
bool &mrBool; bool &mrBool;
public: public:
explicit IdleBool( bool &rBool ) : explicit IdleBool( bool &rBool ) :
Idle(), mrBool( rBool ) Idle( "IdleBool" ), mrBool( rBool )
{ {
SetPriority( TaskPriority::LOWEST ); SetPriority( TaskPriority::LOWEST );
Start(); Start();
...@@ -117,7 +123,7 @@ void TimerTest::testIdle() ...@@ -117,7 +123,7 @@ void TimerTest::testIdle()
{ {
bool bTriggered = false; bool bTriggered = false;
IdleBool aTest( bTriggered ); IdleBool aTest( bTriggered );
Scheduler::ProcessTaskScheduling( true ); while ( Application::Reschedule() );
CPPUNIT_ASSERT_MESSAGE("idle triggered", bTriggered); CPPUNIT_ASSERT_MESSAGE("idle triggered", bTriggered);
} }
...@@ -148,7 +154,7 @@ class TimerBool : public Timer ...@@ -148,7 +154,7 @@ class TimerBool : public Timer
bool &mrBool; bool &mrBool;
public: public:
TimerBool( sal_uLong nMS, bool &rBool ) : TimerBool( sal_uLong nMS, bool &rBool ) :
Timer(), mrBool( rBool ) Timer( "TimerBool" ), mrBool( rBool )
{ {
SetTimeout( nMS ); SetTimeout( nMS );
Start(); Start();
...@@ -180,17 +186,26 @@ void TimerTest::testDurations() ...@@ -180,17 +186,26 @@ void TimerTest::testDurations()
class AutoTimerCount : public AutoTimer class AutoTimerCount : public AutoTimer
{ {
sal_Int32 &mrCount; sal_Int32 &mrCount;
const sal_Int32 mnMaxCount;
public: public:
AutoTimerCount( sal_uLong nMS, sal_Int32 &rCount ) : AutoTimerCount( sal_uLong nMS, sal_Int32 &rCount,
AutoTimer(), mrCount( rCount ) const sal_Int32 nMaxCount = -1 )
: AutoTimer( "AutoTimerCount" )
, mrCount( rCount )
, mnMaxCount( nMaxCount )
{ {
SetTimeout( nMS ); SetTimeout( nMS );
Start(); Start();
mrCount = 0; mrCount = 0;
} }
virtual void Invoke() override virtual void Invoke() override
{ {
mrCount++; ++mrCount;
CPPUNIT_ASSERT( mnMaxCount < 0 || mrCount <= mnMaxCount );
if ( mrCount == mnMaxCount )
Stop();
} }
}; };
...@@ -296,11 +311,22 @@ void TimerTest::testMultiAutoTimers() ...@@ -296,11 +311,22 @@ void TimerTest::testMultiAutoTimers()
} }
#endif // TEST_TIMERPRECISION #endif // TEST_TIMERPRECISION
void TimerTest::testAutoTimerStop()
{
sal_Int32 nTimerCount = 0;
const sal_Int32 nMaxCount = 5;
AutoTimerCount aAutoTimer( 0, nTimerCount, nMaxCount );
while ( nMaxCount != nTimerCount )
Application::Yield();
CPPUNIT_ASSERT( !aAutoTimer.IsActive() );
CPPUNIT_ASSERT( !Application::Reschedule() );
}
class YieldTimer : public Timer class YieldTimer : public Timer
{ {
public: public:
explicit YieldTimer( sal_uLong nMS ) : Timer() explicit YieldTimer( sal_uLong nMS ) : Timer( "YieldTimer" )
{ {
SetTimeout( nMS ); SetTimeout( nMS );
Start(); Start();
...@@ -312,7 +338,7 @@ public: ...@@ -312,7 +338,7 @@ public:
} }
}; };
void TimerTest::testRecursiveTimer() void TimerTest::testNestedTimer()
{ {
sal_Int32 nCount = 0; sal_Int32 nCount = 0;
YieldTimer aCount(5); YieldTimer aCount(5);
...@@ -328,7 +354,7 @@ class SlowCallbackTimer : public Timer ...@@ -328,7 +354,7 @@ class SlowCallbackTimer : public Timer
bool &mbSlow; bool &mbSlow;
public: public:
SlowCallbackTimer( sal_uLong nMS, bool &bBeenSlow ) : SlowCallbackTimer( sal_uLong nMS, bool &bBeenSlow ) :
Timer(), mbSlow( bBeenSlow ) Timer( "SlowCallbackTimer" ), mbSlow( bBeenSlow )
{ {
SetTimeout( nMS ); SetTimeout( nMS );
Start(); Start();
...@@ -362,7 +388,7 @@ class TriggerIdleFromIdle : public Idle ...@@ -362,7 +388,7 @@ class TriggerIdleFromIdle : public Idle
TriggerIdleFromIdle* mpOther; TriggerIdleFromIdle* mpOther;
public: public:
explicit TriggerIdleFromIdle( bool* pTriggered, TriggerIdleFromIdle* pOther ) : explicit TriggerIdleFromIdle( bool* pTriggered, TriggerIdleFromIdle* pOther ) :
Idle(), mpTriggered(pTriggered), mpOther(pOther) Idle( "TriggerIdleFromIdle" ), mpTriggered(pTriggered), mpOther(pOther)
{ {
} }
virtual void Invoke() override virtual void Invoke() override
...@@ -388,6 +414,79 @@ void TimerTest::testTriggerIdleFromIdle() ...@@ -388,6 +414,79 @@ void TimerTest::testTriggerIdleFromIdle()
CPPUNIT_ASSERT_MESSAGE("idle triggered", bTriggered2); CPPUNIT_ASSERT_MESSAGE("idle triggered", bTriggered2);
} }
class IdleInvokedReStart : public Idle
{
sal_Int32 &mrCount;
public:
IdleInvokedReStart( sal_Int32 &rCount )
: Idle( "IdleInvokedReStart" ), mrCount( rCount )
{
Start();
}
virtual void Invoke() override
{
mrCount++;
if ( mrCount < 2 )
Start();
}
};
void TimerTest::testInvokedReStart()
{
sal_Int32 nCount = 0;
IdleInvokedReStart aIdle( nCount );
while ( Application::Reschedule() );
CPPUNIT_ASSERT_EQUAL( nCount, sal_Int32(2) );
}
class IdleSerializer : public Idle
{
sal_uInt32 mnPosition;
sal_uInt32 &mrProcesed;
public:
IdleSerializer( const sal_Char *pDebugName,
sal_uInt32 nPosition, sal_uInt32 &rProcesed )
: Idle( pDebugName )
, mnPosition( nPosition )
, mrProcesed( rProcesed )
{
Start();
}
virtual void Invoke() override
{
++mrProcesed;
CPPUNIT_ASSERT_EQUAL_MESSAGE( "Ignored prio", mnPosition, mrProcesed );
}
};
void TimerTest::testPriority()
{
// scope, so tasks are deleted
{
// 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 );
while ( Application::Reschedule() );
CPPUNIT_ASSERT_EQUAL_MESSAGE( "Not all idles processed", sal_uInt32(2), nProcessed );
}
{
// 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 );
while ( Application::Reschedule() );
CPPUNIT_ASSERT_EQUAL_MESSAGE( "Not all idles processed", sal_uInt32(2), nProcessed );
}
}
CPPUNIT_TEST_SUITE_REGISTRATION(TimerTest); CPPUNIT_TEST_SUITE_REGISTRATION(TimerTest);
CPPUNIT_PLUGIN_IMPLEMENT(); CPPUNIT_PLUGIN_IMPLEMENT();
......
...@@ -19,13 +19,58 @@ ...@@ -19,13 +19,58 @@
#include <svdata.hxx> #include <svdata.hxx>
#include <tools/time.hxx> #include <tools/time.hxx>
#include <vcl/scheduler.hxx> #include <vcl/idle.hxx>
#include <saltimer.hxx> #include <saltimer.hxx>
#include <salinst.hxx> #include <salinst.hxx>
#include <comphelper/profilezone.hxx> #include <comphelper/profilezone.hxx>
namespace { namespace {
const sal_uInt64 MaximumTimeoutMs = 1000 * 60; // 1 minute const sal_uInt64 MaximumTimeoutMs = 1000 * 60; // 1 minute
template< typename charT, typename traits >
inline std::basic_ostream<charT, traits> & operator <<(
std::basic_ostream<charT, traits> & stream, const Task& task )
{
stream << "a: " << task.IsActive() << " p: " << (int) task.GetPriority();
const sal_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 >
inline 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: " << (int) timer.GetPriority();
const sal_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 >
inline std::basic_ostream<charT, traits> & operator <<(
std::basic_ostream<charT, traits> & stream, const Idle& idle )
{
return stream << static_cast<const Timer*>( &idle );
}
} }
void ImplSchedulerData::Invoke() void ImplSchedulerData::Invoke()
...@@ -140,6 +185,7 @@ bool Scheduler::ProcessTaskScheduling( bool bIdle ) ...@@ -140,6 +185,7 @@ bool Scheduler::ProcessTaskScheduling( bool bIdle )
!pSchedulerData->mpTask->ReadyForSchedule( bIdle, nTime ) || !pSchedulerData->mpTask->ReadyForSchedule( bIdle, nTime ) ||
!pSchedulerData->mpTask->IsActive()) !pSchedulerData->mpTask->IsActive())
continue; continue;
if (!pMostUrgent) if (!pMostUrgent)
pMostUrgent = pSchedulerData; pMostUrgent = pSchedulerData;
else else
...@@ -156,7 +202,8 @@ bool Scheduler::ProcessTaskScheduling( bool bIdle ) ...@@ -156,7 +202,8 @@ bool Scheduler::ProcessTaskScheduling( bool bIdle )
{ {
::comphelper::ProfileZone aZone( pMostUrgent->GetDebugName() ); ::comphelper::ProfileZone aZone( pMostUrgent->GetDebugName() );
SAL_INFO("vcl.schedule", "Invoke task " << pMostUrgent->GetDebugName()); SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() << " "
<< pMostUrgent << " invoke " << *pMostUrgent->mpTask );
pMostUrgent->mnUpdateTime = nTime; pMostUrgent->mnUpdateTime = nTime;
pMostUrgent->Invoke(); pMostUrgent->Invoke();
...@@ -192,6 +239,18 @@ sal_uInt64 Scheduler::CalculateMinimumTimeout( bool &bHasActiveIdles ) ...@@ -192,6 +239,18 @@ sal_uInt64 Scheduler::CalculateMinimumTimeout( bool &bHasActiveIdles )
pSchedulerData = pSVData->mpFirstSchedulerData; pSchedulerData = pSVData->mpFirstSchedulerData;
while ( pSchedulerData ) while ( pSchedulerData )
{ {
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)" );
ImplSchedulerData *pNext = pSchedulerData->mpNext; ImplSchedulerData *pNext = pSchedulerData->mpNext;
// Should Task be released from scheduling? // Should Task be released from scheduling?
...@@ -216,10 +275,6 @@ sal_uInt64 Scheduler::CalculateMinimumTimeout( bool &bHasActiveIdles ) ...@@ -216,10 +275,6 @@ sal_uInt64 Scheduler::CalculateMinimumTimeout( bool &bHasActiveIdles )
sal_uInt64 nOldMinPeriod = nMinPeriod; sal_uInt64 nOldMinPeriod = nMinPeriod;
nMinPeriod = pSchedulerData->mpTask->UpdateMinPeriod( nMinPeriod = pSchedulerData->mpTask->UpdateMinPeriod(
nOldMinPeriod, nTime ); nOldMinPeriod, nTime );
SAL_INFO("vcl.schedule", "Have active timer '" <<
pSchedulerData->GetDebugName() <<
"' update min period from " << nOldMinPeriod <<
" to " << nMinPeriod);
assert( nMinPeriod <= nOldMinPeriod ); assert( nMinPeriod <= nOldMinPeriod );
if ( nMinPeriod > nOldMinPeriod ) if ( nMinPeriod > nOldMinPeriod )
{ {
...@@ -229,11 +284,7 @@ sal_uInt64 Scheduler::CalculateMinimumTimeout( bool &bHasActiveIdles ) ...@@ -229,11 +284,7 @@ sal_uInt64 Scheduler::CalculateMinimumTimeout( bool &bHasActiveIdles )
} }
} }
else else
{
SAL_INFO("vcl.schedule", "Have active idle '" <<
pSchedulerData->GetDebugName() << "'");
bHasActiveIdles = true; bHasActiveIdles = true;
}
} }
pPrevSchedulerData = pSchedulerData; pPrevSchedulerData = pSchedulerData;
} }
...@@ -309,15 +360,22 @@ void Task::Start() ...@@ -309,15 +360,22 @@ void Task::Start()
pPrev->mpNext = mpSchedulerData; pPrev->mpNext = mpSchedulerData;
else else
pSVData->mpFirstSchedulerData = mpSchedulerData; pSVData->mpFirstSchedulerData = mpSchedulerData;
SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks()
<< " " << mpSchedulerData << " added " << *this );
} }
else
SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks()
<< " " << mpSchedulerData << " restarted " << *this );
mpSchedulerData->mbDelete = false; mpSchedulerData->mbDelete = false;
mpSchedulerData->mnUpdateTime = tools::Time::GetSystemTicks(); mpSchedulerData->mnUpdateTime = tools::Time::GetSystemTicks();
} }
void Task::Stop() void Task::Stop()
{ {
SAL_INFO_IF( mbActive, "vcl.schedule", tools::Time::GetSystemTicks()
<< " " << mpSchedulerData << " stopped " << *this );
mbActive = false; mbActive = false;
if ( mpSchedulerData ) if ( mpSchedulerData )
mpSchedulerData->mbDelete = true; mpSchedulerData->mbDelete = true;
} }
......
...@@ -516,9 +516,9 @@ inline bool ImplYield(bool i_bWait, bool i_bAllEvents, sal_uLong const nReleased ...@@ -516,9 +516,9 @@ inline bool ImplYield(bool i_bWait, bool i_bAllEvents, sal_uLong const nReleased
return bHasActiveIdles || eResult == SalYieldResult::EVENT; return bHasActiveIdles || eResult == SalYieldResult::EVENT;
} }
void Application::Reschedule( bool i_bAllEvents ) bool Application::Reschedule( bool i_bAllEvents )
{ {
ImplYield(false, i_bAllEvents, 0); return ImplYield(false, i_bAllEvents, 0);
} }
void Scheduler::ProcessEventsToSignal(bool& bSignal) void Scheduler::ProcessEventsToSignal(bool& bSignal)
...@@ -541,6 +541,30 @@ void Scheduler::ProcessEventsToIdle() ...@@ -541,6 +541,30 @@ void Scheduler::ProcessEventsToIdle()
break; break;
} }
} }
#if OSL_DEBUG_LEVEL > 0
// If we yield from a non-main thread we just can guarantee that all idle
// 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() )
return;
const ImplSchedulerData* pSchedulerData = ImplGetSVData()->mpFirstSchedulerData;
bool bAnyIdle = false;
while ( pSchedulerData )
{
if ( pSchedulerData->mpTask && !pSchedulerData->mbInScheduler )
{
Idle *pIdle = dynamic_cast<Idle*>( pSchedulerData->mpTask );
if ( pIdle && pIdle->IsActive() )
{
bAnyIdle = true;
SAL_WARN( "vcl.schedule", "Unprocessed Idle: " << pIdle->GetDebugName() );
}
}
pSchedulerData = pSchedulerData->mpNext;
}
assert( !bAnyIdle );
#endif
} }
extern "C" { extern "C" {
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
#include "KDEXLib.hxx" #include "KDEXLib.hxx"
#include "KDESalDisplay.hxx" #include "KDESalDisplay.hxx"
#include <QtGui/QApplication>
#include <QtCore/QThread>
#include <QX11Info> #include <QX11Info>
using namespace com::sun::star; using namespace com::sun::star;
...@@ -58,4 +60,9 @@ SalX11Display* KDESalInstance::CreateDisplay() const ...@@ -58,4 +60,9 @@ SalX11Display* KDESalInstance::CreateDisplay() const
return new SalKDEDisplay( QX11Info::display() ); return new SalKDEDisplay( QX11Info::display() );
} }
bool KDESalInstance::IsMainThread() const
{
return qApp->thread() == QThread::currentThread();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
...@@ -26,18 +26,20 @@ class SalFrame; ...@@ -26,18 +26,20 @@ class SalFrame;
class KDESalInstance : public X11SalInstance class KDESalInstance : public X11SalInstance
{ {
protected: protected:
virtual SalX11Display* CreateDisplay() const override; virtual SalX11Display* CreateDisplay() const override;
public: public:
explicit KDESalInstance(SalYieldMutex* pMutex); explicit KDESalInstance(SalYieldMutex* pMutex);
virtual SalFrame* CreateFrame( SalFrame* pParent, SalFrameStyleFlags nStyle ) override; virtual SalFrame* CreateFrame( SalFrame* pParent, SalFrameStyleFlags nStyle ) override;
virtual bool hasNativeFileSelection() const override { return true; } virtual bool hasNativeFileSelection() const override { return true; }
virtual css::uno::Reference< css::ui::dialogs::XFilePicker2 > virtual css::uno::Reference< css::ui::dialogs::XFilePicker2 >
createFilePicker( const css::uno::Reference< createFilePicker( const css::uno::Reference<
css::uno::XComponentContext >& ) override; css::uno::XComponentContext >& ) override;
virtual bool IsMainThread() const override;
}; };
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
...@@ -188,10 +188,7 @@ void ImplSalYieldMutexAcquireWithWait() ...@@ -188,10 +188,7 @@ void ImplSalYieldMutexAcquireWithWait()
if ( !pInst ) if ( !pInst )
return; return;
DWORD nThreadId = GetCurrentThreadId(); if ( pInst->IsMainThread() )
SalData* pSalData = GetSalData();
if ( pSalData->mnAppThreadId == nThreadId )
{ {
// tdf#96887 If this is the main thread, then we must wait for two things: // tdf#96887 If this is the main thread, then we must wait for two things:
// - the mpSalYieldMutex being freed // - the mpSalYieldMutex being freed
...@@ -594,13 +591,18 @@ ImplSalYield( bool bWait, bool bHandleAllCurrentEvents ) ...@@ -594,13 +591,18 @@ ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
SalYieldResult::TIMEOUT; SalYieldResult::TIMEOUT;
} }
bool WinSalInstance::IsMainThread() const
{
const SalData* pSalData = GetSalData();
return pSalData->mnAppThreadId == GetCurrentThreadId();
}
SalYieldResult WinSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong const nReleased) SalYieldResult WinSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong const nReleased)
{ {
SalYieldResult eDidWork = SalYieldResult::TIMEOUT; SalYieldResult eDidWork = SalYieldResult::TIMEOUT;
// NOTE: if nReleased != 0 this will be called without SolarMutex // NOTE: if nReleased != 0 this will be called without SolarMutex
// so don't do anything dangerous before releasing it here // so don't do anything dangerous before releasing it here
SalYieldMutex* pYieldMutex = mpSalYieldMutex; SalYieldMutex* pYieldMutex = mpSalYieldMutex;
SalData* pSalData = GetSalData();
DWORD nCurThreadId = GetCurrentThreadId(); DWORD nCurThreadId = GetCurrentThreadId();
sal_uLong const nCount = (nReleased != 0) sal_uLong const nCount = (nReleased != 0)
? nReleased ? nReleased
...@@ -611,7 +613,7 @@ SalYieldResult WinSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents, ...@@ -611,7 +613,7 @@ SalYieldResult WinSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents,
pYieldMutex->release(); pYieldMutex->release();
n--; n--;
} }
if ( pSalData->mnAppThreadId != nCurThreadId ) if ( !IsMainThread() )
{ {
// #97739# A SendMessage call blocks until the called thread (here: the main thread) // #97739# A SendMessage call blocks until the called thread (here: the main thread)
// returns. During a yield however, messages are processed in the main thread that might // returns. During a yield however, messages are processed in the main thread that might
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment