diff --git a/include/Timer.hpp b/include/Timer.hpp index a109303..9991a91 100644 --- a/include/Timer.hpp +++ b/include/Timer.hpp @@ -1,9 +1,11 @@ #ifndef TIMER_HPP #define TIMER_HPP + #include // sigset_t #include // timer_t + class Timer { @@ -20,14 +22,18 @@ public: const time_t initExpr_sec = 0, const long initExpr_nsec = 0 ); + void wait(); void stopTimer(); void gracefulStop(); + private: + void notifyAndRemove( const timespec t ); + // after turning on all warnings, gcc reports that the class has pointer // data members (time_t, which is a long int by the way) so copy ctor and // assign op shall be declared @@ -39,7 +45,6 @@ private: timer_t m_timerId; bool m_periodic; bool m_running; - sigset_t m_mask; }; // class Timer diff --git a/include/TimerThread.hpp b/include/TimerThreadMultimap.hpp similarity index 85% rename from include/TimerThread.hpp rename to include/TimerThreadMultimap.hpp index e9cbc85..09a55f4 100644 --- a/include/TimerThread.hpp +++ b/include/TimerThreadMultimap.hpp @@ -1,5 +1,5 @@ -#ifndef TIMERTHREAD_HPP -#define TIMERTHREAD_HPP +#ifndef TIMERTHREAD_MULTIMAP_HPP +#define TIMERTHREAD_MULTIMAP_HPP #include "Thread.hpp" @@ -23,7 +23,7 @@ public: -class TimerThread : public Thread +class TimerThreadMultimap : public Thread { private: @@ -36,9 +36,9 @@ private: public: - TimerThread(); + TimerThreadMultimap(); - virtual ~TimerThread(); + virtual ~TimerThreadMultimap(); void addTimerUser( TimerUser* user, const time_t expiration, @@ -81,7 +81,7 @@ private: ConditionVariable m_condVar; std::multimap< timespec, UserEntry, timespec_cmp> m_users; -}; // class TimerThread +}; // class TimerThreadMultimap -#endif // TIMERTHREAD_HPP +#endif // TIMERTHREAD_MULTIMAP_HPP diff --git a/src/Timer.cpp b/src/Timer.cpp index 1ae4dc9..8768bae 100644 --- a/src/Timer.cpp +++ b/src/Timer.cpp @@ -7,32 +7,23 @@ #include // strerror -/// @note not used now +/// @note not used now, all signals are caught by sigwaitinfo // static void sigHandler(int sig, siginfo_t *si, void *uc) // { -// TRACE_STATIC; -// +// TRACE_STATIC; // } struct sigaction& sigActionInit( struct sigaction &sigAct, const int signal ) { sigAct.sa_flags = SA_SIGINFO; - // sigAct.sa_sigaction = sigHandler; +// sigAct.sa_sigaction = sigHandler; sigemptyset( &sigAct.sa_mask ); sigaddset( &sigAct.sa_mask, signal ); - sigaction( signal, &sigAct, 0 ); +// sigaction( signal, &sigAct, 0 ); return sigAct; } -sigset_t& sigSetInit( sigset_t &sigSet, const int signal ) -{ - sigemptyset( &sigSet ); - sigaddset(&sigSet, signal ); - sigprocmask(SIG_SETMASK, &sigSet, NULL); - return sigSet; -} - Timer::Timer( const int signal ) : m_signal( signal ) @@ -40,7 +31,6 @@ Timer::Timer( const int signal ) , m_timerId( 0 ) , m_periodic( false ) , m_running( true ) - , m_mask( sigSetInit( m_mask, m_signal ) ) { TRACE; } @@ -49,7 +39,12 @@ Timer::~Timer() { TRACE; - sigprocmask(SIG_UNBLOCK, &m_mask, NULL); + struct itimerspec its; + its.it_value.tv_sec = 0; + its.it_value.tv_nsec = 0; + timer_settime( m_timerId, 0, &its, 0 ); + +// pthread_sigmask( SIG_UNBLOCK, &m_mask, 0 ); } @@ -92,9 +87,12 @@ void Timer::wait() sigwaitinfo( &(m_sigAction.sa_mask), &sigInfo); + if ( not m_running ) return; tidp = (long*)sigInfo.si_value.sival_ptr; -// LOG( Logger::FINEST, ( std::string( "Timer expired with ID: " ) + -// stringify( (timer_t)*tidp ) ).c_str() ); + +// LOG( Logger::FINEST, ( std::string( "got signal: " ) + +// stringify( sigInfo.si_signo ) + +// " from: " + stringify( (timer_t)*tidp ) ).c_str() ); timerExpired(); @@ -102,9 +100,12 @@ void Timer::wait() while ( m_running ) { sigwaitinfo( &(m_sigAction.sa_mask), &sigInfo); + if ( not m_running ) return; tidp = (long*)sigInfo.si_value.sival_ptr; -// LOG( Logger::FINEST, ( std::string( "Timer expired with ID:" ) + -// stringify( (timer_t)*tidp ) ).c_str() ); + +// LOG( Logger::FINEST, ( std::string( "got periodic signal: " ) + +// stringify( sigInfo.si_signo ) + +// " from: " + stringify( (timer_t)*tidp ) ).c_str() ); periodicTimerExpired(); } @@ -117,11 +118,14 @@ void Timer::stopTimer() { TRACE; + // disarm timer struct itimerspec its; its.it_value.tv_sec = 0; its.it_value.tv_nsec = 0; timer_settime( m_timerId, 0, &its, 0 ); + m_running = false; + /// @note sigwaitinfo waiting state, don't forget to send a last signal } @@ -129,5 +133,7 @@ void Timer::gracefulStop() { TRACE; + // if it's periodic, use stopTimer m_running = false; + /// @note sigwaitinfo waiting state, don't forget to send a last signal } diff --git a/src/TimerThread.cpp b/src/TimerThreadMultimap.cpp similarity index 88% rename from src/TimerThread.cpp rename to src/TimerThreadMultimap.cpp index c3bcf29..33971df 100644 --- a/src/TimerThread.cpp +++ b/src/TimerThreadMultimap.cpp @@ -1,4 +1,4 @@ -#include "TimerThread.hpp" +#include "TimerThreadMultimap.hpp" #include "Common.hpp" #include "ScopedLock.hpp" @@ -7,7 +7,7 @@ -TimerThread::TimerThread() +TimerThreadMultimap::TimerThreadMultimap() : Thread() , m_mutex() , m_condVar( m_mutex ) @@ -17,13 +17,13 @@ TimerThread::TimerThread() } -TimerThread::~TimerThread() +TimerThreadMultimap::~TimerThreadMultimap() { TRACE; } -void TimerThread::addTimerUser( TimerUser* user, +void TimerThreadMultimap::addTimerUser( TimerUser* user, const time_t expiration, const time_t periodTime ) { @@ -36,7 +36,7 @@ void TimerThread::addTimerUser( TimerUser* user, } -void TimerThread::addTimerUser( TimerUser* user, +void TimerThreadMultimap::addTimerUser( TimerUser* user, const timespec expiration, const timespec periodTime ) { @@ -55,7 +55,7 @@ void TimerThread::addTimerUser( TimerUser* user, } -bool TimerThread::removeTimerUser( TimerUser* timerUser ) +bool TimerThreadMultimap::removeTimerUser( TimerUser* timerUser ) { TRACE; ScopedLock sl( m_mutex ); @@ -78,7 +78,7 @@ bool TimerThread::removeTimerUser( TimerUser* timerUser ) } -void TimerThread::stop() +void TimerThreadMultimap::stop() { TRACE; ScopedLock sl( m_mutex ); @@ -87,7 +87,7 @@ void TimerThread::stop() } -void TimerThread::notifyAndRemove( const timespec t ) +void TimerThreadMultimap::notifyAndRemove( const timespec t ) { TRACE; ScopedLock sl( m_mutex ); @@ -119,7 +119,7 @@ void TimerThread::notifyAndRemove( const timespec t ) } -void* TimerThread::run( void ) +void* TimerThreadMultimap::run( void ) { TRACE; timespec nextExpiration; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 458b4c0..c7db2d9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -25,7 +25,7 @@ if(CXXTEST_FOUND) test_Semaphore.hpp test_Timer.hpp test_Common.hpp - test_TimerThread.hpp + test_TimerThreadMultimap.hpp ) target_link_libraries(test CppUtils gcov) endif() diff --git a/test/run_test.sh b/test/run_test.sh index 89c4e95..06e1499 100755 --- a/test/run_test.sh +++ b/test/run_test.sh @@ -37,7 +37,7 @@ if [ ! -e $test ]; then exit 1 fi -echo -e "${pre}Reset & remove files${post}" +echo -e "${pre}Reset & remove lcov, $test.out and core files${post}" # coverage lcov --directory . -z rm -f ./lcov.info @@ -73,6 +73,7 @@ if [ $retval -ne 0 ]; then if [ $retval -ne 137 ]; then echo -e "${pre}Failed checks:${post}" + # TODO print the previous line too cat $test.out | grep "Error:" | awk -F"/test/" '{ print $2 }' fi @@ -85,6 +86,7 @@ if [ $retval -ne 0 ]; then gdb $test $cores -ex "set width 1000" -ex "thread apply all bt" -ex q > gdb.out ./gdb_out_parser.pl gdb.out + echo "" if yesno "run 'gdb $test $cores' ?"; then gdb $test $cores fi @@ -114,3 +116,8 @@ echo -e "${pre}Checking the coverage results${post}" echo -e "${pre}Checking leak results${post}" ./leak_check.pl leak.log +if [ $? == 1 ]; then + if yesno "run 'vim leak.log' ?"; then + vim leak.log + fi +fi diff --git a/test/test_Timer.hpp b/test/test_Timer.hpp index 9098d43..6a31b68 100644 --- a/test/test_Timer.hpp +++ b/test/test_Timer.hpp @@ -12,68 +12,6 @@ class TestTimer : public CxxTest::TestSuite { -private: - - class DummyTimer : public Timer - { - public: - - DummyTimer(int maxPeriodicCount = 5, const int signal = SIGALRM) - : Timer(signal) - , m_counter(0) - , m_maxPeriodicCount(maxPeriodicCount) - { - TRACE; - } - - void timerExpired() - { - TRACE; - m_counter += 100; - } - - void periodicTimerExpired() - { - TRACE; - static int count = 0; - m_counter++; - count++; - if ( count >= m_maxPeriodicCount ) { - stopTimer(); - } - } - - int m_counter; - - private: - - int m_maxPeriodicCount; - - }; - - -public: - - void testBasic( void ) - { - TEST_HEADER; - - DummyTimer t; - t.createTimer(2); - t.wait(); - TS_ASSERT_EQUALS( t.m_counter, 100 ); - } - - void testBasicPeriodic( void ) - { - TEST_HEADER; - - DummyTimer t; - t.createTimer(2,0,1); - t.wait(); - TS_ASSERT_EQUALS( t.m_counter, 105 ); - } - private: class DummyTimerThread : public Timer, public Thread @@ -86,13 +24,26 @@ private: const long interval_nsec = 0, const time_t initExpr_sec = 0, const long initExpr_nsec = 0 ) - : Timer(signal) - , m_counter(0) - , m_maxPeriodicCount(maxPeriodicCount) - , m_interval_sec(interval_sec) - , m_interval_nsec(interval_nsec) - , m_initExpr_sec(initExpr_sec) - , m_initExpr_nsec(initExpr_nsec) + : Timer( signal ) + , m_counter( 0 ) + , m_maxPeriodicCount( maxPeriodicCount ) + , m_signal ( signal ) + , m_interval_sec( interval_sec ) + , m_interval_nsec( interval_nsec ) + , m_initExpr_sec( initExpr_sec ) + , m_initExpr_nsec( initExpr_nsec ) + { + TRACE; + LOG( Logger::FINEST, ( std::string( "params: " ) + + stringify( maxPeriodicCount ) + " " + + stringify( signal ) + " " + + stringify( interval_sec ) + " " + + stringify( interval_nsec ) + " " + + stringify( initExpr_sec ) + " " + + stringify( initExpr_nsec ) ).c_str() ); + } + + ~DummyTimerThread() { TRACE; } @@ -105,7 +56,7 @@ private: createTimer( m_interval_sec, m_interval_nsec, m_initExpr_sec, - m_initExpr_nsec); + m_initExpr_nsec ); wait(); return 0; } @@ -130,11 +81,18 @@ private: } } + void stop() + { + stopTimer(); + sendSignal( m_signal ); + } + int m_counter; private: int m_maxPeriodicCount; + int m_signal; time_t m_interval_sec; long m_interval_nsec; time_t m_initExpr_sec; @@ -152,7 +110,7 @@ private: sigset_t set; sigemptyset( &set ); sigaddset( &set, SIGALRM ); - sigprocmask(SIG_BLOCK, &set, NULL); + pthread_sigmask( SIG_BLOCK, &set, 0 ); DummyTimerThread t(5); @@ -161,6 +119,56 @@ private: t.join(); TS_ASSERT_EQUALS( t.m_counter, 100 ); + + pthread_sigmask( SIG_UNBLOCK, &set, 0 ); + } + + void testStopTimerThread( void ) + { + TEST_HEADER; + + // the main thread shall ignore the SIGALRM + sigset_t set; + sigemptyset( &set ); + sigaddset( &set, SIGALRM ); + pthread_sigmask( SIG_BLOCK, &set, 0 ); + + DummyTimerThread t(5, SIGALRM, 10); + + t.start(); + sleep(1); + t.stop(); + t.join(); + + TS_ASSERT_EQUALS( t.m_counter, 0 ); + + pthread_sigmask( SIG_UNBLOCK, &set, 0 ); + } + + void testPeriodicTimerThread( void ) + { + TEST_HEADER; + + // the main thread shall ignore the SIGALRM + sigset_t set; + sigemptyset( &set ); + sigaddset( &set, SIGALRM ); + pthread_sigmask( SIG_BLOCK, &set, 0 ); + + DummyTimerThread t( 1000, + SIGALRM, + 1, 0, + 1, 0 ); + + t.start(); + sleep(4); + t.stop(); + sleep(2); + t.join(); + + TS_ASSERT_EQUALS( t.m_counter, 102 ); + + pthread_sigmask( SIG_UNBLOCK, &set, 0 ); } void testCustomSignal( void ) @@ -173,20 +181,17 @@ private: sigset_t set; sigemptyset( &set ); sigaddset( &set, customSignal ); - sigprocmask( SIG_BLOCK, &set, NULL); - + pthread_sigmask( SIG_BLOCK, &set, 0 ); DummyTimerThread t( 5, customSignal ); t.start(); -// timespec ts = { 4, 0 }; -// nanosleep( &ts , 0 ); sleep(4); t.join(); TS_ASSERT_EQUALS( t.m_counter, 100 ); - sigprocmask( SIG_UNBLOCK, &set, NULL ); + pthread_sigmask( SIG_UNBLOCK, &set, 0 ); } void testTimerThreadHighFreq( void ) @@ -197,26 +202,29 @@ private: sigset_t set; sigemptyset( &set ); sigaddset( &set, SIGALRM ); - sigprocmask(SIG_BLOCK, &set, NULL); + pthread_sigmask( SIG_BLOCK, &set, 0 ); int nano = 1000000000; // 10^9 - int freq = 80000; - DummyTimerThread t(INT_MAX - 1, - SIGALRM, - 1, 0, - 0, nano / freq ); + int freq = 200; + DummyTimerThread t( INT_MAX - 1, + SIGALRM, + 1, 0, + 0, nano / freq ); t.start(); - int circle = 10; + int circle = 4; sleep( 1 + circle ); - t.gracefulStop(); + t.stop(); t.join(); // expected 800000 + 100 got 795510 // accurcy: ~ > 99.5% TS_ASSERT_DELTA ( t.m_counter, 100 + freq * circle, (100 + freq * circle) * 0.995); - sigprocmask( SIG_UNBLOCK, &set, NULL ); + LOG( Logger::FINEST, ( std::string( "got: " ) + stringify( t.m_counter ) + " " + + "expected: " + stringify( 100 + freq * circle ) ).c_str() ); + + pthread_sigmask( SIG_UNBLOCK, &set, 0 ); } }; diff --git a/test/test_TimerThread.hpp b/test/test_TimerThreadMultimap.hpp similarity index 85% rename from test/test_TimerThread.hpp rename to test/test_TimerThreadMultimap.hpp index df6aecf..7e45eb7 100644 --- a/test/test_TimerThread.hpp +++ b/test/test_TimerThreadMultimap.hpp @@ -2,16 +2,16 @@ #include "Fixture.hpp" -#define private public // reach TimerThread's private multimap +#define private public // reach TimerThreadMultimap's private multimap -#include "TimerThread.hpp" +#include "TimerThreadMultimap.hpp" #include -class TestTimerThread : public CxxTest::TestSuite +class TestTimerThreadMultimap : public CxxTest::TestSuite { private: @@ -37,7 +37,7 @@ public: void testBasic( void ) { TEST_HEADER; - TimerThread* tt = new TimerThread(); + TimerThreadMultimap* tt = new TimerThreadMultimap(); tt->start(); sleep(1); @@ -57,7 +57,7 @@ public: void testBasicTimeSpec( void ) { TEST_HEADER; - TimerThread* tt = new TimerThread(); + TimerThreadMultimap* tt = new TimerThreadMultimap(); tt->start(); sleep(1); @@ -78,7 +78,7 @@ public: void testPeriodic( void ) { TEST_HEADER; - TimerThread* tt = new TimerThread(); + TimerThreadMultimap* tt = new TimerThreadMultimap(); tt->start(); sleep(1); @@ -100,8 +100,8 @@ public: TEST_HEADER; // Logger::getInstance()->setLogLevel(Logger::DEBUG); - - TimerThread* tt = new TimerThread(); + + TimerThreadMultimap* tt = new TimerThreadMultimap(); tt->start(); sleep(1); @@ -120,7 +120,7 @@ public: tt->stop(); sleep(1); - TS_ASSERT_EQUALS( user->m_counter, 4 ); // 4 times + TS_ASSERT_EQUALS( user->m_counter, 104 ); // 4 times // TS_ASSERT_EQUALS( user2->m_counter, perMinute*4 ); delete tt; @@ -131,7 +131,7 @@ public: void testDestroyed( void ) { TEST_HEADER; - TimerThread* tt = new TimerThread(); + TimerThreadMultimap* tt = new TimerThreadMultimap(); tt->start(); sleep(1); @@ -151,7 +151,7 @@ public: void testRemoved( void ) { TEST_HEADER; - TimerThread* tt = new TimerThread(); + TimerThreadMultimap* tt = new TimerThreadMultimap(); tt->start(); sleep(1); @@ -194,7 +194,7 @@ public: void testRemovedMultiple( void ) { TEST_HEADER; - TimerThread* tt = new TimerThread(); + TimerThreadMultimap* tt = new TimerThreadMultimap(); tt->start(); sleep(1); diff --git a/test/valgrind.supp b/test/valgrind.supp index c5eca56..5cb07ab 100644 --- a/test/valgrind.supp +++ b/test/valgrind.supp @@ -39,12 +39,6 @@ } -{ - comparing void pointers - Memcheck:Addr4 - fun:_ZN11TimerThread15removeTimerUserEPv -} - { thread with signal Memcheck:Leak @@ -102,3 +96,23 @@ fun:timer_create@@GLIBC_2.3.3 } + +{ + disarm timer + Memcheck:Param + timer_settime(value) + fun:timer_settime@@GLIBC_2.3.3 + fun:_ZN5TimerD1Ev + ... + fun:main +} + +{ + disarm timer 2 + Memcheck:Param + timer_settime(value) + fun:timer_settime@@GLIBC_2.3.3 + fun:_ZN5Timer9stopTimerEv + ... + fun:main +}