1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 * Timers are evil beasts across platforms...
13 #include <test/bootstrapfixture.hxx>
15 #include <osl/thread.hxx>
18 #include <vcl/timer.hxx>
19 #include <vcl/idle.hxx>
20 #include <vcl/svapp.hxx>
21 #include <vcl/scheduler.hxx>
23 #include <salinst.hxx>
25 // #define TEST_WATCHDOG
27 // Enables timer tests that appear to provoke windows under load unduly.
28 //#define TEST_TIMERPRECISION
30 /// Avoid our timer tests just wedging the build if they fail.
31 class WatchDog
: public osl::Thread
33 sal_Int32
const mnSeconds
;
35 explicit WatchDog(sal_Int32 nSeconds
) :
41 virtual void SAL_CALL
run() override
43 osl::Thread::wait( std::chrono::seconds(mnSeconds
) );
44 fprintf(stderr
, "ERROR: WatchDog timer thread expired, failing the test!\n");
46 CPPUNIT_ASSERT_MESSAGE("watchdog triggered", false);
50 static WatchDog
* aWatchDog
= new WatchDog( 120 ); // random high number in secs
52 class TimerTest
: public test::BootstrapFixture
55 TimerTest() : BootstrapFixture(true, false) {}
58 void testIdleMainloop();
63 #ifdef TEST_TIMERPRECISION
65 void testMultiAutoTimers();
67 void testAutoTimerStop();
68 void testNestedTimer();
69 void testSlowTimerCallback();
70 void testTriggerIdleFromIdle();
71 void testInvokedReStart();
73 void testRoundRobin();
75 CPPUNIT_TEST_SUITE(TimerTest
);
76 CPPUNIT_TEST(testIdle
);
77 CPPUNIT_TEST(testIdleMainloop
);
79 CPPUNIT_TEST(testWatchdog
);
81 CPPUNIT_TEST(testDurations
);
82 #ifdef TEST_TIMERPRECISION
83 CPPUNIT_TEST(testAutoTimer
);
84 CPPUNIT_TEST(testMultiAutoTimers
);
86 CPPUNIT_TEST(testAutoTimerStop
);
87 CPPUNIT_TEST(testNestedTimer
);
88 CPPUNIT_TEST(testSlowTimerCallback
);
89 CPPUNIT_TEST(testTriggerIdleFromIdle
);
90 CPPUNIT_TEST(testInvokedReStart
);
91 CPPUNIT_TEST(testPriority
);
92 CPPUNIT_TEST(testRoundRobin
);
94 CPPUNIT_TEST_SUITE_END();
98 void TimerTest::testWatchdog()
100 // out-wait the watchdog.
101 osl::Thread::wait( std::chrono::seconds(12) );
106 class IdleBool
: public Idle
110 explicit IdleBool( bool &rBool
) :
111 Idle( "IdleBool" ), mrBool( rBool
)
113 SetPriority( TaskPriority::LOWEST
);
117 virtual void Invoke() override
120 Application::EndYield();
124 void TimerTest::testIdle()
126 bool bTriggered
= false;
127 IdleBool
aTest( bTriggered
);
128 Scheduler::ProcessEventsToIdle();
129 CPPUNIT_ASSERT_MESSAGE("idle triggered", bTriggered
);
132 void TimerTest::testIdleMainloop()
134 bool bTriggered
= false;
135 IdleBool
aTest( bTriggered
);
136 // coverity[loop_top] - Application::Yield allows the timer to fire and toggle bDone
139 ImplSVData
* pSVData
= ImplGetSVData();
141 // can't test this via Application::Yield since this
142 // also processes all tasks directly via the scheduler.
143 pSVData
->maAppData
.mnDispatchLevel
++;
144 pSVData
->mpDefInst
->DoYield(true, false);
145 pSVData
->maAppData
.mnDispatchLevel
--;
147 CPPUNIT_ASSERT_MESSAGE("mainloop idle triggered", bTriggered
);
150 class TimerBool
: public Timer
154 TimerBool( sal_uLong nMS
, bool &rBool
) :
155 Timer( "TimerBool" ), mrBool( rBool
)
161 virtual void Invoke() override
164 Application::EndYield();
168 void TimerTest::testDurations()
170 static const sal_uLong aDurations
[] = { 0, 1, 500, 1000 };
171 for (size_t i
= 0; i
< SAL_N_ELEMENTS( aDurations
); i
++)
174 TimerBool
aTimer( aDurations
[i
], bDone
);
175 // coverity[loop_top] - Application::Yield allows the timer to fire and toggle bDone
178 Application::Yield();
184 class AutoTimerCount
: public AutoTimer
187 const sal_Int32 mnMaxCount
;
190 AutoTimerCount( sal_uLong nMS
, sal_Int32
&rCount
,
191 const sal_Int32 nMaxCount
= -1 )
192 : AutoTimer( "AutoTimerCount" )
194 , mnMaxCount( nMaxCount
)
201 virtual void Invoke() override
204 CPPUNIT_ASSERT( mnMaxCount
< 0 || mrCount
<= mnMaxCount
);
205 if ( mrCount
== mnMaxCount
)
210 #ifdef TEST_TIMERPRECISION
212 void TimerTest::testAutoTimer()
214 const sal_Int32 nDurationMs
= 30;
215 const sal_Int32 nEventsCount
= 5;
216 const double exp
= (nDurationMs
* nEventsCount
);
218 sal_Int32 nCount
= 0;
219 std::ostringstream msg
;
221 // Repeat when we have random latencies.
222 // This is expected on non-realtime OSes.
223 for (int i
= 0; i
< 10; ++i
)
225 const auto start
= std::chrono::high_resolution_clock::now();
227 AutoTimerCount
aCount(nDurationMs
, nCount
);
228 while (nCount
< nEventsCount
) {
229 Application::Yield();
232 const auto end
= std::chrono::high_resolution_clock::now();
233 double dur
= std::chrono::duration
<double, std::milli
>(end
- start
).count();
235 msg
<< std::setprecision(2) << std::fixed
236 << "periodic multi-timer - dur: "
237 << dur
<< " (" << exp
<< ") ms." << std::endl
;
239 // +/- 20% should be reasonable enough a margin.
240 if (dur
>= (exp
* 0.8) && dur
<= (exp
* 1.2))
247 CPPUNIT_FAIL(msg
.str().c_str());
250 void TimerTest::testMultiAutoTimers()
252 // The behavior of the timers change drastically
253 // when multiple timers are present.
254 // The worst, in my tests, is when two
255 // timers with 1ms period exist with a
256 // third of much longer period.
258 const sal_Int32 nDurationMsX
= 5;
259 const sal_Int32 nDurationMsY
= 10;
260 const sal_Int32 nDurationMs
= 40;
261 const sal_Int32 nEventsCount
= 5;
262 const double exp
= (nDurationMs
* nEventsCount
);
263 const double expX
= (exp
/ nDurationMsX
);
264 const double expY
= (exp
/ nDurationMsY
);
266 sal_Int32 nCountX
= 0;
267 sal_Int32 nCountY
= 0;
268 sal_Int32 nCount
= 0;
269 std::ostringstream msg
;
271 // Repeat when we have random latencies.
272 // This is expected on non-realtime OSes.
273 for (int i
= 0; i
< 10; ++i
)
279 const auto start
= std::chrono::high_resolution_clock::now();
280 AutoTimerCount
aCountX(nDurationMsX
, nCountX
);
281 AutoTimerCount
aCountY(nDurationMsY
, nCountY
);
283 AutoTimerCount
aCount(nDurationMs
, nCount
);
284 // coverity[loop_top] - Application::Yield allows the timer to fire and toggle nCount
285 while (nCount
< nEventsCount
) {
286 Application::Yield();
289 const auto end
= std::chrono::high_resolution_clock::now();
290 double dur
= std::chrono::duration
<double, std::milli
>(end
- start
).count();
292 msg
<< std::setprecision(2) << std::fixed
<< "periodic multi-timer - dur: "
293 << dur
<< " (" << exp
<< ") ms, nCount: " << nCount
294 << " (" << nEventsCount
<< "), nCountX: " << nCountX
295 << " (" << expX
<< "), nCountY: " << nCountY
296 << " (" << expY
<< ")." << std::endl
;
298 // +/- 20% should be reasonable enough a margin.
299 if (dur
>= (exp
* 0.8) && dur
<= (exp
* 1.2) &&
300 nCountX
>= (expX
* 0.8) && nCountX
<= (expX
* 1.2) &&
301 nCountY
>= (expY
* 0.8) && nCountY
<= (expY
* 1.2))
308 CPPUNIT_FAIL(msg
.str().c_str());
310 #endif // TEST_TIMERPRECISION
312 void TimerTest::testAutoTimerStop()
314 sal_Int32 nTimerCount
= 0;
315 const sal_Int32 nMaxCount
= 5;
316 AutoTimerCount
aAutoTimer( 0, nTimerCount
, nMaxCount
);
317 // coverity[loop_top] - Application::Yield allows the timer to fire and increment TimerCount
318 while (nMaxCount
!= nTimerCount
)
319 Application::Yield();
320 CPPUNIT_ASSERT( !aAutoTimer
.IsActive() );
321 CPPUNIT_ASSERT( !Application::Reschedule() );
325 class YieldTimer
: public Timer
328 explicit YieldTimer( sal_uLong nMS
) : Timer( "YieldTimer" )
333 virtual void Invoke() override
335 for (int i
= 0; i
< 100; i
++)
336 Application::Yield();
340 void TimerTest::testNestedTimer()
342 sal_Int32 nCount
= 0;
343 YieldTimer
aCount(5);
344 AutoTimerCount
aCountUp( 3, nCount
);
345 // coverity[loop_top] - Application::Yield allows the timer to fire and increment nCount
347 Application::Yield();
351 class SlowCallbackTimer
: public Timer
355 SlowCallbackTimer( sal_uLong nMS
, bool &bBeenSlow
) :
356 Timer( "SlowCallbackTimer" ), mbSlow( bBeenSlow
)
362 virtual void Invoke() override
364 osl::Thread::wait( std::chrono::seconds(1) );
369 void TimerTest::testSlowTimerCallback()
371 bool bBeenSlow
= false;
372 sal_Int32 nCount
= 0;
373 AutoTimerCount
aHighFreq(1, nCount
);
374 SlowCallbackTimer
aSlow(250, bBeenSlow
);
375 // coverity[loop_top] - Application::Yield allows the timer to fire and toggle bBeenSlow
377 Application::Yield();
378 // coverity[loop_top] - Application::Yield allows the timer to fire and increment nCount
380 Application::Yield();
384 class TriggerIdleFromIdle
: public Idle
387 TriggerIdleFromIdle
* mpOther
;
389 explicit TriggerIdleFromIdle( bool* pTriggered
, TriggerIdleFromIdle
* pOther
) :
390 Idle( "TriggerIdleFromIdle" ), mpTriggered(pTriggered
), mpOther(pOther
)
393 virtual void Invoke() override
398 Application::Yield();
404 void TimerTest::testTriggerIdleFromIdle()
406 bool bTriggered1
= false;
407 bool bTriggered2
= false;
408 TriggerIdleFromIdle
aTest2( &bTriggered2
, nullptr );
409 TriggerIdleFromIdle
aTest1( &bTriggered1
, &aTest2
);
411 Application::Yield();
412 CPPUNIT_ASSERT_MESSAGE("idle not triggered", bTriggered1
);
413 CPPUNIT_ASSERT_MESSAGE("idle not triggered", bTriggered2
);
417 class IdleInvokedReStart
: public Idle
421 IdleInvokedReStart( sal_Int32
&rCount
)
422 : Idle( "IdleInvokedReStart" ), mrCount( rCount
)
426 virtual void Invoke() override
434 void TimerTest::testInvokedReStart()
436 sal_Int32 nCount
= 0;
437 IdleInvokedReStart
aIdle( nCount
);
438 Scheduler::ProcessEventsToIdle();
439 CPPUNIT_ASSERT_EQUAL( sal_Int32(2), nCount
);
443 class IdleSerializer
: public Idle
445 sal_uInt32
const mnPosition
;
446 sal_uInt32
&mrProcesed
;
448 IdleSerializer(const sal_Char
*pDebugName
, TaskPriority ePrio
,
449 sal_uInt32 nPosition
, sal_uInt32
&rProcesed
)
451 , mnPosition( nPosition
)
452 , mrProcesed( rProcesed
)
457 virtual void Invoke() override
460 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Ignored prio", mnPosition
, mrProcesed
);
464 void TimerTest::testPriority()
466 // scope, so tasks are deleted
468 // Start: 1st Idle low, 2nd high
469 sal_uInt32 nProcessed
= 0;
470 IdleSerializer
aLowPrioIdle("IdleSerializer LowPrio",
471 TaskPriority::LOWEST
, 2, nProcessed
);
472 IdleSerializer
aHighPrioIdle("IdleSerializer HighPrio",
473 TaskPriority::HIGHEST
, 1, nProcessed
);
474 Scheduler::ProcessEventsToIdle();
475 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Not all idles processed", sal_uInt32(2), nProcessed
);
479 // Start: 1st Idle high, 2nd low
480 sal_uInt32 nProcessed
= 0;
481 IdleSerializer
aHighPrioIdle("IdleSerializer HighPrio",
482 TaskPriority::HIGHEST
, 1, nProcessed
);
483 IdleSerializer
aLowPrioIdle("IdleSerializer LowPrio",
484 TaskPriority::LOWEST
, 2, nProcessed
);
485 Scheduler::ProcessEventsToIdle();
486 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Not all idles processed", sal_uInt32(2), nProcessed
);
491 class TestAutoIdleRR
: public AutoIdle
495 DECL_LINK( IdleRRHdl
, Timer
*, void );
498 TestAutoIdleRR( sal_uInt32
&rCount
,
499 const sal_Char
*pDebugName
)
500 : AutoIdle( pDebugName
)
503 CPPUNIT_ASSERT_EQUAL( sal_uInt32(0), mrCount
);
504 SetInvokeHandler( LINK( this, TestAutoIdleRR
, IdleRRHdl
) );
509 IMPL_LINK_NOARG(TestAutoIdleRR
, IdleRRHdl
, Timer
*, void)
516 void TimerTest::testRoundRobin()
518 sal_uInt32 nCount1
= 0, nCount2
= 0;
519 TestAutoIdleRR
aIdle1( nCount1
, "TestAutoIdleRR aIdle1" ),
520 aIdle2( nCount2
, "TestAutoIdleRR aIdle2" );
521 while ( Application::Reschedule() )
523 CPPUNIT_ASSERT( nCount1
== nCount2
|| nCount1
- 1 == nCount2
);
524 CPPUNIT_ASSERT( nCount1
<= 3 );
525 CPPUNIT_ASSERT( nCount2
<= 3 );
527 CPPUNIT_ASSERT( 3 == nCount1
&& 3 == nCount2
);
530 CPPUNIT_TEST_SUITE_REGISTRATION(TimerTest
);
532 CPPUNIT_PLUGIN_IMPLEMENT();
534 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */