@ -15,272 +15,187 @@ class TimerTest : public testing::Test {
protected :
std : : unique_ptr < MockTimeEnv > mock_env_ ;
# if defined(OS_MACOSX) && !defined(NDEBUG)
// On some platforms (MacOS) pthread_cond_timedwait does not appear
// to release the lock for other threads to operate if the deadline time
// is already passed. This is a problem for tests in general because
// TimedWait calls are a bad abstraction: the deadline parameter is
// usually computed from Env time, but is interpreted in real clock time.
// Since this test doesn't even pretend to use clock times, we have
// to mock TimedWait to ensure it yields.
void SetUp ( ) override {
ROCKSDB_NAMESPACE : : SyncPoint : : GetInstance ( ) - > DisableProcessing ( ) ;
ROCKSDB_NAMESPACE : : SyncPoint : : GetInstance ( ) - > ClearAllCallBacks ( ) ;
ROCKSDB_NAMESPACE : : SyncPoint : : GetInstance ( ) - > SetCallBack (
" InstrumentedCondVar::TimedWaitInternal " , [ & ] ( void * arg ) {
uint64_t * time_us = reinterpret_cast < uint64_t * > ( arg ) ;
if ( * time_us < mock_env_ - > RealNowMicros ( ) ) {
* time_us = mock_env_ - > RealNowMicros ( ) + 1000 ;
}
} ) ;
ROCKSDB_NAMESPACE : : SyncPoint : : GetInstance ( ) - > EnableProcessing ( ) ;
}
# endif // OS_MACOSX && !NDEBUG
const uint64_t kSecond = 1000000 ; // 1sec = 1000000us
void SetUp ( ) override { mock_env_ - > InstallTimedWaitFixCallback ( ) ; }
} ;
TEST_F ( TimerTest , SingleScheduleOnceTest ) {
const int kIterations = 1 ;
uint64_t time_counter = 0 ;
mock_env_ - > set_current_time ( 0 ) ;
InstrumentedMutex mutex ;
InstrumentedCondVar test_cv ( & mutex ) ;
const int kInitDelaySec = 1 ;
int mock_time_sec = 0 ;
mock_env_ - > set_current_time ( mock_time_sec ) ;
Timer timer ( mock_env_ . get ( ) ) ;
int count = 0 ;
timer . Add (
[ & ] {
InstrumentedMutexLock l ( & mutex ) ;
count + + ;
if ( count > = kIterations ) {
test_cv . SignalAll ( ) ;
}
} ,
" fn_sch_test " , 1 * kSecond , 0 ) ;
timer . Add ( [ & ] { count + + ; } , " fn_sch_test " , kInitDelaySec * kSecond , 0 ) ;
ASSERT_TRUE ( timer . Start ( ) ) ;
ASSERT_EQ ( 0 , count ) ;
// Wait for execution to finish
{
InstrumentedMutexLock l ( & mutex ) ;
while ( count < kIterations ) {
time_counter + = kSecond ;
mock_env_ - > set_current_time ( time_counter ) ;
test_cv . TimedWait ( time_counter ) ;
}
}
mock_time_sec + = kInitDelaySec ;
timer . TEST_WaitForRun ( [ & ] { mock_env_ - > set_current_time ( mock_time_sec ) ; } ) ;
ASSERT_EQ ( 1 , count ) ;
ASSERT_TRUE ( timer . Shutdown ( ) ) ;
ASSERT_EQ ( 1 , count ) ;
}
TEST_F ( TimerTest , MultipleScheduleOnceTest ) {
const int kIterations = 1 ;
uint64_t time_counter = 0 ;
mock_env_ - > set_current_time ( 0 ) ;
InstrumentedMutex mutex1 ;
InstrumentedCondVar test_cv1 ( & mutex1 ) ;
const int kInitDelay1Sec = 1 ;
const int kInitDelay2Sec = 3 ;
int mock_time_sec = 0 ;
mock_env_ - > set_current_time ( mock_time_sec ) ;
Timer timer ( mock_env_ . get ( ) ) ;
int count1 = 0 ;
timer . Add (
[ & ] {
InstrumentedMutexLock l ( & mutex1 ) ;
count1 + + ;
if ( count1 > = kIterations ) {
test_cv1 . SignalAll ( ) ;
}
} ,
" fn_sch_test1 " , 1 * kSecond , 0 ) ;
timer . Add ( [ & ] { count1 + + ; } , " fn_sch_test1 " , kInitDelay1Sec * kSecond , 0 ) ;
InstrumentedMutex mutex2 ;
InstrumentedCondVar test_cv2 ( & mutex2 ) ;
int count2 = 0 ;
timer . Add (
[ & ] {
InstrumentedMutexLock l ( & mutex2 ) ;
count2 + = 5 ;
if ( count2 > = kIterations ) {
test_cv2 . SignalAll ( ) ;
}
} ,
" fn_sch_test2 " , 3 * kSecond , 0 ) ;
timer . Add ( [ & ] { count2 + + ; } , " fn_sch_test2 " , kInitDelay2Sec * kSecond , 0 ) ;
ASSERT_TRUE ( timer . Start ( ) ) ;
ASSERT_EQ ( 0 , count1 ) ;
ASSERT_EQ ( 0 , count2 ) ;
// Wait for execution to finish
{
InstrumentedMutexLock l ( & mutex1 ) ;
while ( count1 < kIterations ) {
time_counter + = kSecond ;
mock_env_ - > set_current_time ( time_counter ) ;
test_cv1 . TimedWait ( time_counter ) ;
}
}
mock_time_sec = kInitDelay1Sec ;
timer . TEST_WaitForRun ( [ & ] { mock_env_ - > set_current_time ( mock_time_sec ) ; } ) ;
// Wait for execution to finish
{
InstrumentedMutexLock l ( & mutex2 ) ;
while ( count2 < kIterations ) {
time_counter + = kSecond ;
mock_env_ - > set_current_time ( time_counter ) ;
test_cv2 . TimedWait ( time_counter ) ;
}
}
ASSERT_EQ ( 1 , count1 ) ;
ASSERT_EQ ( 0 , count2 ) ;
ASSERT_TRUE ( timer . Shutdown ( ) ) ;
mock_time_sec = kInitDelay2Sec ;
timer . TEST_WaitForRun ( [ & ] { mock_env_ - > set_current_time ( mock_time_sec ) ; } ) ;
ASSERT_EQ ( 1 , count1 ) ;
ASSERT_EQ ( 5 , count2 ) ;
ASSERT_EQ ( 1 , count2 ) ;
ASSERT_TRUE ( timer . Shutdown ( ) ) ;
}
TEST_F ( TimerTest , SingleScheduleRepeatedlyTest ) {
const int kIterations = 5 ;
uint64_t time_counter = 0 ;
mock_env_ - > set_current_time ( 0 ) ;
InstrumentedMutex mutex ;
InstrumentedCondVar test_cv ( & mutex ) ;
const int kInitDelaySec = 1 ;
const int kRepeatSec = 1 ;
int mock_time_sec = 0 ;
mock_env_ - > set_current_time ( mock_time_sec ) ;
Timer timer ( mock_env_ . get ( ) ) ;
int count = 0 ;
timer . Add (
[ & ] {
InstrumentedMutexLock l ( & mutex ) ;
count + + ;
if ( count > = kIterations ) {
test_cv . SignalAll ( ) ;
}
} ,
" fn_sch_test " , 1 * kSecond , 1 * kSecond ) ;
timer . Add ( [ & ] { count + + ; } , " fn_sch_test " , kInitDelaySec * kSecond ,
kRepeatSec * kSecond ) ;
ASSERT_TRUE ( timer . Start ( ) ) ;
ASSERT_EQ ( 0 , count ) ;
mock_time_sec + = kInitDelaySec ;
timer . TEST_WaitForRun ( [ & ] { mock_env_ - > set_current_time ( mock_time_sec ) ; } ) ;
ASSERT_EQ ( 1 , count ) ;
// Wait for execution to finish
{
InstrumentedMutexLock l ( & mutex ) ;
while ( count < kIterations ) {
time_counter + = kSecond ;
mock_env_ - > set_current_time ( time_counter ) ;
test_cv . TimedWait ( time_counter ) ;
}
for ( int i = 1 ; i < kIterations ; i + + ) {
mock_time_sec + = kRepeatSec ;
timer . TEST_WaitForRun ( [ & ] { mock_env_ - > set_current_time ( mock_time_sec ) ; } ) ;
}
ASSERT_EQ ( kIterations , count ) ;
ASSERT_TRUE ( timer . Shutdown ( ) ) ;
ASSERT_EQ ( 5 , count ) ;
}
TEST_F ( TimerTest , MultipleScheduleRepeatedlyTest ) {
uint64_t time_counter = 0 ;
mock_env_ - > set_current_time ( 0 ) ;
const int kInitDelay1Sec = 0 ;
const int kInitDelay2Sec = 1 ;
const int kInitDelay3Sec = 0 ;
const int kRepeatSec = 2 ;
const int kLargeRepeatSec = 100 ;
const int kIterations = 5 ;
int mock_time_sec = 0 ;
mock_env_ - > set_current_time ( mock_time_sec ) ;
Timer timer ( mock_env_ . get ( ) ) ;
InstrumentedMutex mutex1 ;
InstrumentedCondVar test_cv1 ( & mutex1 ) ;
const int kIterations1 = 5 ;
int count1 = 0 ;
timer . Add (
[ & ] {
InstrumentedMutexLock l ( & mutex1 ) ;
count1 + + ;
if ( count1 > = kIterations1 ) {
test_cv1 . SignalAll ( ) ;
}
} ,
" fn_sch_test1 " , 0 , 2 * kSecond ) ;
timer . Add ( [ & ] { count1 + + ; } , " fn_sch_test1 " , kInitDelay1Sec * kSecond ,
kRepeatSec * kSecond ) ;
InstrumentedMutex mutex2 ;
InstrumentedCondVar test_cv2 ( & mutex2 ) ;
const int kIterations2 = 5 ;
int count2 = 0 ;
timer . Add (
[ & ] {
InstrumentedMutexLock l ( & mutex2 ) ;
count2 + + ;
if ( count2 > = kIterations2 ) {
test_cv2 . SignalAll ( ) ;
}
} ,
" fn_sch_test2 " , 1 * kSecond , 2 * kSecond ) ;
timer . Add ( [ & ] { count2 + + ; } , " fn_sch_test2 " , kInitDelay2Sec * kSecond ,
kRepeatSec * kSecond ) ;
// Add a function with relatively large repeat interval
int count3 = 0 ;
timer . Add ( [ & ] { count3 + + ; } , " fn_sch_test3 " , kInitDelay3Sec * kSecond ,
kLargeRepeatSec * kSecond ) ;
ASSERT_TRUE ( timer . Start ( ) ) ;
ASSERT_EQ ( 0 , count2 ) ;
ASSERT_EQ ( 0 , count3 ) ;
// Wait for execution to finish
{
InstrumentedMutexLock l ( & mutex1 ) ;
while ( count1 < kIterations1 ) {
time_counter + = kSecond ;
mock_env_ - > set_current_time ( time_counter ) ;
test_cv1 . TimedWait ( time_counter ) ;
}
for ( ; count1 < kIterations ; mock_time_sec + + ) {
timer . TEST_WaitForRun ( [ & ] { mock_env_ - > set_current_time ( mock_time_sec ) ; } ) ;
ASSERT_EQ ( ( mock_time_sec + 2 ) / kRepeatSec , count1 ) ;
ASSERT_EQ ( ( mock_time_sec + 1 ) / kRepeatSec , count2 ) ;
// large interval function should only run once (the first one).
ASSERT_EQ ( 1 , count3 ) ;
}
timer . Cancel ( " fn_sch_test1 " ) ;
// Wait for execution to finish
{
InstrumentedMutexLock l ( & mutex2 ) ;
while ( count2 < kIterations2 ) {
time_counter + = kSecond ;
mock_env_ - > set_current_time ( time_counter ) ;
test_cv2 . TimedWait ( time_counter ) ;
}
}
mock_time_sec + + ;
timer . TEST_WaitForRun ( [ & ] { mock_env_ - > set_current_time ( mock_time_sec ) ; } ) ;
ASSERT_EQ ( kIterations , count1 ) ;
ASSERT_EQ ( kIterations , count2 ) ;
ASSERT_EQ ( 1 , count3 ) ;
timer . Cancel ( " fn_sch_test2 " ) ;
ASSERT_TRUE ( timer . Shutdown ( ) ) ;
ASSERT_EQ ( kIterations , count1 ) ;
ASSERT_EQ ( kIterations , count2 ) ;
// execute the long interval one
mock_time_sec = kLargeRepeatSec ;
timer . TEST_WaitForRun ( [ & ] { mock_env_ - > set_current_time ( mock_time_sec ) ; } ) ;
ASSERT_EQ ( 2 , count3 ) ;
ASSERT_EQ ( count1 , 5 ) ;
ASSERT_EQ ( count2 , 5 ) ;
ASSERT_TRUE ( timer . Shutdown ( ) ) ;
}
TEST_F ( TimerTest , AddAfterStartTest ) {
const int kIterations = 5 ;
InstrumentedMutex mutex ;
InstrumentedCondVar test_cv ( & mutex ) ;
const int kInitDelaySec = 1 ;
const int kRepeatSec = 1 ;
// wait timer to run and then add a new job
SyncPoint : : GetInstance ( ) - > LoadDependency (
{ { " Timer::Run::Waiting " , " TimerTest:AddAfterStartTest:1 " } } ) ;
SyncPoint : : GetInstance ( ) - > EnableProcessing ( ) ;
mock_env_ - > set_current_time ( 0 ) ;
int mock_time_sec = 0 ;
mock_env_ - > set_current_time ( mock_time_sec ) ;
Timer timer ( mock_env_ . get ( ) ) ;
ASSERT_TRUE ( timer . Start ( ) ) ;
TEST_SYNC_POINT ( " TimerTest:AddAfterStartTest:1 " ) ;
int count = 0 ;
timer . Add (
[ & ] {
InstrumentedMutexLock l ( & mutex ) ;
count + + ;
if ( count > = kIterations ) {
test_cv . SignalAll ( ) ;
}
} ,
" fn_sch_test " , 1 * kSecond , 1 * kSecond ) ;
timer . Add ( [ & ] { count + + ; } , " fn_sch_test " , kInitDelaySec * kSecond ,
kRepeatSec * kSecond ) ;
ASSERT_EQ ( 0 , count ) ;
// Wait for execution to finish
uint64_t time_counter = 0 ;
{
InstrumentedMutexLock l ( & mutex ) ;
while ( count < kIterations ) {
time_counter + = kSecond ;
mock_env_ - > set_current_time ( time_counter ) ;
test_cv . TimedWait ( time_counter ) ;
}
mock_time_sec + = kInitDelaySec ;
timer . TEST_WaitForRun ( [ & ] { mock_env_ - > set_current_time ( mock_time_sec ) ; } ) ;
ASSERT_EQ ( 1 , count ) ;
for ( int i = 1 ; i < kIterations ; i + + ) {
mock_time_sec + = kRepeatSec ;
timer . TEST_WaitForRun ( [ & ] { mock_env_ - > set_current_time ( mock_time_sec ) ; } ) ;
}
ASSERT_EQ ( kIterations , count ) ;
ASSERT_TRUE ( timer . Shutdown ( ) ) ;
ASSERT_EQ ( kIterations , count ) ;
}
TEST_F ( TimerTest , CancelRunningTask ) {
@ -356,35 +271,86 @@ TEST_F(TimerTest, ShutdownRunningTask) {
delete value ;
}
TEST_F ( TimerTest , AddSameFuncNameTest ) {
mock_env_ - > set_current_time ( 0 ) ;
TEST_F ( TimerTest , AddSameFuncName ) {
const int kInitDelaySec = 1 ;
const int kRepeat1Sec = 5 ;
const int kRepeat2Sec = 4 ;
int mock_time_sec = 0 ;
mock_env_ - > set_current_time ( mock_time_sec ) ;
Timer timer ( mock_env_ . get ( ) ) ;
ASSERT_TRUE ( timer . Start ( ) ) ;
int func_counter1 = 0 ;
timer . Add ( [ & ] { func_counter1 + + ; } , " duplicated_func " , 1 * kSecond ,
5 * kSecond ) ;
timer . Add ( [ & ] { func_counter1 + + ; } , " duplicated_func " ,
kInitDelaySec * kSecond , kRepeat1Sec * kSecond ) ;
int func2_counter = 0 ;
timer . Add ( [ & ] { func2_counter + + ; } , " func2 " , 1 * kSecond , 4 * kSecond ) ;
timer . Add ( [ & ] { func2_counter + + ; } , " func2 " , kInitDelaySec * kSecond ,
kRepeat2Sec * kSecond ) ;
// New function with the same name should override the existing one
int func_counter2 = 0 ;
timer . Add ( [ & ] { func_counter2 + + ; } , " duplicated_func " , 1 * kSecond ,
5 * kSecond ) ;
timer . Add ( [ & ] { func_counter2 + + ; } , " duplicated_func " ,
kInitDelaySec * kSecond , kRepeat1Sec * kSecond ) ;
ASSERT_EQ ( 0 , func_counter1 ) ;
ASSERT_EQ ( 0 , func2_counter ) ;
ASSERT_EQ ( 0 , func_counter2 ) ;
timer . TEST_WaitForRun ( [ & ] { mock_env_ - > set_current_time ( 1 ) ; } ) ;
mock_time_sec + = kInitDelaySec ;
timer . TEST_WaitForRun ( [ & ] { mock_env_ - > set_current_time ( mock_time_sec ) ; } ) ;
ASSERT_EQ ( func_counter1 , 0 ) ;
ASSERT_EQ ( func2_counter , 1 ) ;
ASSERT_EQ ( func_counter2 , 1 ) ;
ASSERT_EQ ( 0 , func_counter1 ) ;
ASSERT_EQ ( 1 , func2_counter ) ;
ASSERT_EQ ( 1 , func_counter2 ) ;
timer . TEST_WaitForRun ( [ & ] { mock_env_ - > set_current_time ( 6 ) ; } ) ;
mock_time_sec + = kRepeat1Sec ;
timer . TEST_WaitForRun ( [ & ] { mock_env_ - > set_current_time ( mock_time_sec ) ; } ) ;
ASSERT_EQ ( func_counter1 , 0 ) ;
ASSERT_EQ ( func2_counter , 2 ) ;
ASSERT_EQ ( func_counter2 , 2 ) ;
ASSERT_EQ ( 0 , func_counter1 ) ;
ASSERT_EQ ( 2 , func2_counter ) ;
ASSERT_EQ ( 2 , func_counter2 ) ;
ASSERT_TRUE ( timer . Shutdown ( ) ) ;
}
TEST_F ( TimerTest , RepeatIntervalWithFuncRunningTime ) {
const int kInitDelaySec = 1 ;
const int kRepeatSec = 5 ;
const int kFuncRunningTimeSec = 1 ;
int mock_time_sec = 0 ;
mock_env_ - > set_current_time ( mock_time_sec ) ;
Timer timer ( mock_env_ . get ( ) ) ;
ASSERT_TRUE ( timer . Start ( ) ) ;
int func_counter = 0 ;
timer . Add (
[ & ] {
mock_env_ - > set_current_time ( mock_time_sec + kFuncRunningTimeSec ) ;
func_counter + + ;
} ,
" func " , kInitDelaySec * kSecond , kRepeatSec * kSecond ) ;
ASSERT_EQ ( 0 , func_counter ) ;
mock_time_sec + = kInitDelaySec ;
timer . TEST_WaitForRun ( [ & ] { mock_env_ - > set_current_time ( mock_time_sec ) ; } ) ;
ASSERT_EQ ( 1 , func_counter ) ;
// After repeat interval time, the function is not executed, as running
// the function takes some time (`kFuncRunningTimeSec`). The repeat interval
// is the time between ending time of the last call and starting time of the
// next call.
mock_time_sec + = kRepeatSec ;
timer . TEST_WaitForRun ( [ & ] { mock_env_ - > set_current_time ( mock_time_sec ) ; } ) ;
ASSERT_EQ ( 1 , func_counter ) ;
mock_time_sec + = kFuncRunningTimeSec ;
timer . TEST_WaitForRun ( [ & ] { mock_env_ - > set_current_time ( mock_time_sec ) ; } ) ;
ASSERT_EQ ( 2 , func_counter ) ;
ASSERT_TRUE ( timer . Shutdown ( ) ) ;
}