| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
- // This source code is licensed under both the GPLv2 (found in the
- // COPYING file in the root directory) and Apache 2.0 License
- // (found in the LICENSE.Apache file in the root directory).
- #include "util/timer.h"
- #include "db/db_test_util.h"
- #include "rocksdb/file_system.h"
- #include "test_util/mock_time_env.h"
- namespace ROCKSDB_NAMESPACE {
- class TimerTest : public testing::Test {
- public:
- TimerTest()
- : mock_clock_(std::make_shared<MockSystemClock>(SystemClock::Default())) {
- }
- protected:
- std::shared_ptr<MockSystemClock> mock_clock_;
- void SetUp() override { mock_clock_->InstallTimedWaitFixCallback(); }
- const int kUsPerSec = 1000000;
- };
- TEST_F(TimerTest, SingleScheduleOnce) {
- const int kInitDelayUs = 1 * kUsPerSec;
- Timer timer(mock_clock_.get());
- int count = 0;
- timer.Add([&] { count++; }, "fn_sch_test", kInitDelayUs, 0);
- ASSERT_TRUE(timer.Start());
- ASSERT_EQ(0, count);
- // Wait for execution to finish
- timer.TEST_WaitForRun(
- [&] { mock_clock_->SleepForMicroseconds(kInitDelayUs); });
- ASSERT_EQ(1, count);
- ASSERT_TRUE(timer.Shutdown());
- }
- TEST_F(TimerTest, MultipleScheduleOnce) {
- const int kInitDelay1Us = 1 * kUsPerSec;
- const int kInitDelay2Us = 3 * kUsPerSec;
- Timer timer(mock_clock_.get());
- int count1 = 0;
- timer.Add([&] { count1++; }, "fn_sch_test1", kInitDelay1Us, 0);
- int count2 = 0;
- timer.Add([&] { count2++; }, "fn_sch_test2", kInitDelay2Us, 0);
- ASSERT_TRUE(timer.Start());
- ASSERT_EQ(0, count1);
- ASSERT_EQ(0, count2);
- timer.TEST_WaitForRun(
- [&] { mock_clock_->SleepForMicroseconds(kInitDelay1Us); });
- ASSERT_EQ(1, count1);
- ASSERT_EQ(0, count2);
- timer.TEST_WaitForRun([&] {
- mock_clock_->SleepForMicroseconds(kInitDelay2Us - kInitDelay1Us);
- });
- ASSERT_EQ(1, count1);
- ASSERT_EQ(1, count2);
- ASSERT_TRUE(timer.Shutdown());
- }
- TEST_F(TimerTest, SingleScheduleRepeatedly) {
- const int kIterations = 5;
- const int kInitDelayUs = 1 * kUsPerSec;
- const int kRepeatUs = 1 * kUsPerSec;
- Timer timer(mock_clock_.get());
- int count = 0;
- timer.Add([&] { count++; }, "fn_sch_test", kInitDelayUs, kRepeatUs);
- ASSERT_TRUE(timer.Start());
- ASSERT_EQ(0, count);
- timer.TEST_WaitForRun(
- [&] { mock_clock_->SleepForMicroseconds(kInitDelayUs); });
- ASSERT_EQ(1, count);
- // Wait for execution to finish
- for (int i = 1; i < kIterations; i++) {
- timer.TEST_WaitForRun(
- [&] { mock_clock_->SleepForMicroseconds(kRepeatUs); });
- }
- ASSERT_EQ(kIterations, count);
- ASSERT_TRUE(timer.Shutdown());
- }
- TEST_F(TimerTest, MultipleScheduleRepeatedly) {
- const int kIterations = 5;
- const int kInitDelay1Us = 0 * kUsPerSec;
- const int kInitDelay2Us = 1 * kUsPerSec;
- const int kInitDelay3Us = 0 * kUsPerSec;
- const int kRepeatUs = 2 * kUsPerSec;
- const int kLargeRepeatUs = 100 * kUsPerSec;
- Timer timer(mock_clock_.get());
- int count1 = 0;
- timer.Add([&] { count1++; }, "fn_sch_test1", kInitDelay1Us, kRepeatUs);
- int count2 = 0;
- timer.Add([&] { count2++; }, "fn_sch_test2", kInitDelay2Us, kRepeatUs);
- // Add a function with relatively large repeat interval
- int count3 = 0;
- timer.Add([&] { count3++; }, "fn_sch_test3", kInitDelay3Us, kLargeRepeatUs);
- ASSERT_TRUE(timer.Start());
- ASSERT_EQ(0, count2);
- // Wait for execution to finish
- for (int i = 1; i < kIterations * (kRepeatUs / kUsPerSec); i++) {
- timer.TEST_WaitForRun(
- [&] { mock_clock_->SleepForMicroseconds(1 * kUsPerSec); });
- ASSERT_EQ((i + 2) / (kRepeatUs / kUsPerSec), count1);
- ASSERT_EQ((i + 1) / (kRepeatUs / kUsPerSec), 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
- timer.TEST_WaitForRun(
- [&] { mock_clock_->SleepForMicroseconds(1 * kUsPerSec); });
- ASSERT_EQ(kIterations, count1);
- ASSERT_EQ(kIterations, count2);
- ASSERT_EQ(1, count3);
- timer.Cancel("fn_sch_test2");
- ASSERT_EQ(kIterations, count1);
- ASSERT_EQ(kIterations, count2);
- // execute the long interval one
- timer.TEST_WaitForRun([&] {
- mock_clock_->SleepForMicroseconds(
- kLargeRepeatUs - static_cast<int>(mock_clock_->NowMicros()));
- });
- ASSERT_EQ(2, count3);
- ASSERT_TRUE(timer.Shutdown());
- }
- TEST_F(TimerTest, AddAfterStartTest) {
- const int kIterations = 5;
- const int kInitDelayUs = 1 * kUsPerSec;
- const int kRepeatUs = 1 * kUsPerSec;
- // wait timer to run and then add a new job
- SyncPoint::GetInstance()->LoadDependency(
- {{"Timer::Run::Waiting", "TimerTest:AddAfterStartTest:1"}});
- SyncPoint::GetInstance()->EnableProcessing();
- Timer timer(mock_clock_.get());
- ASSERT_TRUE(timer.Start());
- TEST_SYNC_POINT("TimerTest:AddAfterStartTest:1");
- int count = 0;
- timer.Add([&] { count++; }, "fn_sch_test", kInitDelayUs, kRepeatUs);
- ASSERT_EQ(0, count);
- // Wait for execution to finish
- timer.TEST_WaitForRun(
- [&] { mock_clock_->SleepForMicroseconds(kInitDelayUs); });
- ASSERT_EQ(1, count);
- for (int i = 1; i < kIterations; i++) {
- timer.TEST_WaitForRun(
- [&] { mock_clock_->SleepForMicroseconds(kRepeatUs); });
- }
- ASSERT_EQ(kIterations, count);
- ASSERT_TRUE(timer.Shutdown());
- }
- TEST_F(TimerTest, CancelRunningTask) {
- static constexpr char kTestFuncName[] = "test_func";
- const int kRepeatUs = 1 * kUsPerSec;
- Timer timer(mock_clock_.get());
- ASSERT_TRUE(timer.Start());
- int* value = new int;
- *value = 0;
- SyncPoint::GetInstance()->DisableProcessing();
- SyncPoint::GetInstance()->LoadDependency({
- {"TimerTest::CancelRunningTask:test_func:0",
- "TimerTest::CancelRunningTask:BeforeCancel"},
- {"Timer::WaitForTaskCompleteIfNecessary:TaskExecuting",
- "TimerTest::CancelRunningTask:test_func:1"},
- });
- SyncPoint::GetInstance()->EnableProcessing();
- timer.Add(
- [&]() {
- *value = 1;
- TEST_SYNC_POINT("TimerTest::CancelRunningTask:test_func:0");
- TEST_SYNC_POINT("TimerTest::CancelRunningTask:test_func:1");
- },
- kTestFuncName, 0, kRepeatUs);
- port::Thread control_thr([&]() {
- TEST_SYNC_POINT("TimerTest::CancelRunningTask:BeforeCancel");
- timer.Cancel(kTestFuncName);
- // Verify that *value has been set to 1.
- ASSERT_EQ(1, *value);
- delete value;
- value = nullptr;
- });
- mock_clock_->SleepForMicroseconds(kRepeatUs);
- control_thr.join();
- ASSERT_TRUE(timer.Shutdown());
- }
- TEST_F(TimerTest, ShutdownRunningTask) {
- const int kRepeatUs = 1 * kUsPerSec;
- constexpr char kTestFunc1Name[] = "test_func1";
- constexpr char kTestFunc2Name[] = "test_func2";
- Timer timer(mock_clock_.get());
- SyncPoint::GetInstance()->DisableProcessing();
- SyncPoint::GetInstance()->LoadDependency({
- {"TimerTest::ShutdownRunningTest:test_func:0",
- "TimerTest::ShutdownRunningTest:BeforeShutdown"},
- {"Timer::WaitForTaskCompleteIfNecessary:TaskExecuting",
- "TimerTest::ShutdownRunningTest:test_func:1"},
- });
- SyncPoint::GetInstance()->EnableProcessing();
- ASSERT_TRUE(timer.Start());
- int* value = new int;
- *value = 0;
- timer.Add(
- [&]() {
- TEST_SYNC_POINT("TimerTest::ShutdownRunningTest:test_func:0");
- *value = 1;
- TEST_SYNC_POINT("TimerTest::ShutdownRunningTest:test_func:1");
- },
- kTestFunc1Name, 0, kRepeatUs);
- timer.Add([&]() { ++(*value); }, kTestFunc2Name, 0, kRepeatUs);
- port::Thread control_thr([&]() {
- TEST_SYNC_POINT("TimerTest::ShutdownRunningTest:BeforeShutdown");
- timer.Shutdown();
- });
- mock_clock_->SleepForMicroseconds(kRepeatUs);
- control_thr.join();
- delete value;
- }
- TEST_F(TimerTest, AddSameFuncName) {
- const int kInitDelayUs = 1 * kUsPerSec;
- const int kRepeat1Us = 5 * kUsPerSec;
- const int kRepeat2Us = 4 * kUsPerSec;
- Timer timer(mock_clock_.get());
- ASSERT_TRUE(timer.Start());
- int func_counter1 = 0;
- ASSERT_TRUE(timer.Add([&] { func_counter1++; }, "duplicated_func",
- kInitDelayUs, kRepeat1Us));
- int func2_counter = 0;
- ASSERT_TRUE(
- timer.Add([&] { func2_counter++; }, "func2", kInitDelayUs, kRepeat2Us));
- // New function with the same name should fail to add
- int func_counter2 = 0;
- ASSERT_FALSE(timer.Add([&] { func_counter2++; }, "duplicated_func",
- kInitDelayUs, kRepeat1Us));
- ASSERT_EQ(0, func_counter1);
- ASSERT_EQ(0, func2_counter);
- timer.TEST_WaitForRun(
- [&] { mock_clock_->SleepForMicroseconds(kInitDelayUs); });
- ASSERT_EQ(1, func_counter1);
- ASSERT_EQ(1, func2_counter);
- timer.TEST_WaitForRun([&] { mock_clock_->SleepForMicroseconds(kRepeat1Us); });
- ASSERT_EQ(2, func_counter1);
- ASSERT_EQ(2, func2_counter);
- ASSERT_EQ(0, func_counter2);
- ASSERT_TRUE(timer.Shutdown());
- }
- TEST_F(TimerTest, RepeatIntervalWithFuncRunningTime) {
- const int kInitDelayUs = 1 * kUsPerSec;
- const int kRepeatUs = 5 * kUsPerSec;
- const int kFuncRunningTimeUs = 1 * kUsPerSec;
- Timer timer(mock_clock_.get());
- ASSERT_TRUE(timer.Start());
- int func_counter = 0;
- timer.Add(
- [&] {
- mock_clock_->SleepForMicroseconds(kFuncRunningTimeUs);
- func_counter++;
- },
- "func", kInitDelayUs, kRepeatUs);
- ASSERT_EQ(0, func_counter);
- timer.TEST_WaitForRun(
- [&] { mock_clock_->SleepForMicroseconds(kInitDelayUs); });
- ASSERT_EQ(1, func_counter);
- ASSERT_EQ(kInitDelayUs + kFuncRunningTimeUs, mock_clock_->NowMicros());
- // 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.
- uint64_t next_abs_interval_time_us = kInitDelayUs + kRepeatUs;
- timer.TEST_WaitForRun([&] {
- mock_clock_->SetCurrentTime(next_abs_interval_time_us / kUsPerSec);
- });
- ASSERT_EQ(1, func_counter);
- // After the function running time, it's executed again
- timer.TEST_WaitForRun(
- [&] { mock_clock_->SleepForMicroseconds(kFuncRunningTimeUs); });
- ASSERT_EQ(2, func_counter);
- ASSERT_TRUE(timer.Shutdown());
- }
- TEST_F(TimerTest, DestroyRunningTimer) {
- const int kInitDelayUs = 1 * kUsPerSec;
- const int kRepeatUs = 1 * kUsPerSec;
- auto timer_ptr = new Timer(mock_clock_.get());
- int count = 0;
- timer_ptr->Add([&] { count++; }, "fn_sch_test", kInitDelayUs, kRepeatUs);
- ASSERT_TRUE(timer_ptr->Start());
- timer_ptr->TEST_WaitForRun(
- [&] { mock_clock_->SleepForMicroseconds(kInitDelayUs); });
- // delete a running timer should not cause any exception
- delete timer_ptr;
- }
- TEST_F(TimerTest, DestroyTimerWithRunningFunc) {
- const int kRepeatUs = 1 * kUsPerSec;
- auto timer_ptr = new Timer(mock_clock_.get());
- SyncPoint::GetInstance()->DisableProcessing();
- SyncPoint::GetInstance()->LoadDependency({
- {"TimerTest::DestroyTimerWithRunningFunc:test_func:0",
- "TimerTest::DestroyTimerWithRunningFunc:BeforeDelete"},
- {"Timer::WaitForTaskCompleteIfNecessary:TaskExecuting",
- "TimerTest::DestroyTimerWithRunningFunc:test_func:1"},
- });
- SyncPoint::GetInstance()->EnableProcessing();
- ASSERT_TRUE(timer_ptr->Start());
- int count = 0;
- timer_ptr->Add(
- [&]() {
- TEST_SYNC_POINT("TimerTest::DestroyTimerWithRunningFunc:test_func:0");
- count++;
- TEST_SYNC_POINT("TimerTest::DestroyTimerWithRunningFunc:test_func:1");
- },
- "fn_running_test", 0, kRepeatUs);
- port::Thread control_thr([&] {
- TEST_SYNC_POINT("TimerTest::DestroyTimerWithRunningFunc:BeforeDelete");
- delete timer_ptr;
- });
- mock_clock_->SleepForMicroseconds(kRepeatUs);
- control_thr.join();
- }
- } // namespace ROCKSDB_NAMESPACE
- int main(int argc, char** argv) {
- ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
- }
|