]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
2 | // This source code is licensed under both the GPLv2 (found in the | |
3 | // COPYING file in the root directory) and Apache 2.0 License | |
4 | // (found in the LICENSE.Apache file in the root directory). | |
5 | ||
6 | #include <atomic> | |
7 | #include <memory> | |
8 | ||
9 | #include "db/db_test_util.h" | |
f67539c2 TL |
10 | #include "test_util/sync_point.h" |
11 | #include "test_util/testharness.h" | |
11fdf7f2 | 12 | #include "util/repeatable_thread.h" |
11fdf7f2 TL |
13 | |
14 | class RepeatableThreadTest : public testing::Test { | |
15 | public: | |
16 | RepeatableThreadTest() | |
f67539c2 TL |
17 | : mock_env_(new ROCKSDB_NAMESPACE::MockTimeEnv( |
18 | ROCKSDB_NAMESPACE::Env::Default())) {} | |
11fdf7f2 TL |
19 | |
20 | protected: | |
f67539c2 | 21 | std::unique_ptr<ROCKSDB_NAMESPACE::MockTimeEnv> mock_env_; |
11fdf7f2 TL |
22 | }; |
23 | ||
24 | TEST_F(RepeatableThreadTest, TimedTest) { | |
25 | constexpr uint64_t kSecond = 1000000; // 1s = 1000000us | |
26 | constexpr int kIteration = 3; | |
f67539c2 TL |
27 | ROCKSDB_NAMESPACE::Env* env = ROCKSDB_NAMESPACE::Env::Default(); |
28 | ROCKSDB_NAMESPACE::port::Mutex mutex; | |
29 | ROCKSDB_NAMESPACE::port::CondVar test_cv(&mutex); | |
11fdf7f2 TL |
30 | int count = 0; |
31 | uint64_t prev_time = env->NowMicros(); | |
f67539c2 | 32 | ROCKSDB_NAMESPACE::RepeatableThread thread( |
11fdf7f2 | 33 | [&] { |
f67539c2 | 34 | ROCKSDB_NAMESPACE::MutexLock l(&mutex); |
11fdf7f2 TL |
35 | count++; |
36 | uint64_t now = env->NowMicros(); | |
37 | assert(count == 1 || prev_time + 1 * kSecond <= now); | |
38 | prev_time = now; | |
39 | if (count >= kIteration) { | |
40 | test_cv.SignalAll(); | |
41 | } | |
42 | }, | |
43 | "rt_test", env, 1 * kSecond); | |
44 | // Wait for execution finish. | |
45 | { | |
f67539c2 | 46 | ROCKSDB_NAMESPACE::MutexLock l(&mutex); |
11fdf7f2 TL |
47 | while (count < kIteration) { |
48 | test_cv.Wait(); | |
49 | } | |
50 | } | |
51 | ||
52 | // Test cancel | |
53 | thread.cancel(); | |
54 | } | |
55 | ||
56 | TEST_F(RepeatableThreadTest, MockEnvTest) { | |
57 | constexpr uint64_t kSecond = 1000000; // 1s = 1000000us | |
58 | constexpr int kIteration = 3; | |
59 | mock_env_->set_current_time(0); // in seconds | |
60 | std::atomic<int> count{0}; | |
494da23a TL |
61 | |
62 | #if defined(OS_MACOSX) && !defined(NDEBUG) | |
f67539c2 TL |
63 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing(); |
64 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks(); | |
65 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
494da23a TL |
66 | "InstrumentedCondVar::TimedWaitInternal", [&](void* arg) { |
67 | // Obtain the current (real) time in seconds and add 1000 extra seconds | |
68 | // to ensure that RepeatableThread::wait invokes TimedWait with a time | |
69 | // greater than (real) current time. This is to prevent the TimedWait | |
70 | // function from returning immediately without sleeping and releasing | |
71 | // the mutex on certain platforms, e.g. OS X. If TimedWait returns | |
72 | // immediately, the mutex will not be released, and | |
73 | // RepeatableThread::TEST_WaitForRun never has a chance to execute the | |
74 | // callback which, in this case, updates the result returned by | |
75 | // mock_env->NowMicros. Consequently, RepeatableThread::wait cannot | |
76 | // break out of the loop, causing test to hang. The extra 1000 seconds | |
77 | // is a best-effort approach because there seems no reliable and | |
78 | // deterministic way to provide the aforementioned guarantee. By the | |
79 | // time RepeatableThread::wait is called, it is no guarantee that the | |
80 | // delay + mock_env->NowMicros will be greater than the current real | |
81 | // time. However, 1000 seconds should be sufficient in most cases. | |
82 | uint64_t time_us = *reinterpret_cast<uint64_t*>(arg); | |
83 | if (time_us < mock_env_->RealNowMicros()) { | |
84 | *reinterpret_cast<uint64_t*>(arg) = mock_env_->RealNowMicros() + 1000; | |
85 | } | |
86 | }); | |
f67539c2 | 87 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); |
494da23a TL |
88 | #endif // OS_MACOSX && !NDEBUG |
89 | ||
f67539c2 TL |
90 | ROCKSDB_NAMESPACE::RepeatableThread thread( |
91 | [&] { count++; }, "rt_test", mock_env_.get(), 1 * kSecond, 1 * kSecond); | |
11fdf7f2 TL |
92 | for (int i = 1; i <= kIteration; i++) { |
93 | // Bump current time | |
94 | thread.TEST_WaitForRun([&] { mock_env_->set_current_time(i); }); | |
95 | } | |
96 | // Test function should be exectued exactly kIteraion times. | |
97 | ASSERT_EQ(kIteration, count.load()); | |
98 | ||
99 | // Test cancel | |
100 | thread.cancel(); | |
101 | } | |
102 | ||
103 | int main(int argc, char** argv) { | |
104 | ::testing::InitGoogleTest(&argc, argv); | |
105 | ||
106 | return RUN_ALL_TESTS(); | |
107 | } |