1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "RecurrentTimer.h"
18
19 #include <android-base/thread_annotations.h>
20 #include <gtest/gtest.h>
21
22 #include <chrono>
23 #include <memory>
24 #include <mutex>
25
26 namespace android {
27 namespace hardware {
28 namespace automotive {
29 namespace vehicle {
30
31 class RecurrentTimerTest : public testing::Test {
32 public:
getCallback(size_t token)33 std::shared_ptr<RecurrentTimer::Callback> getCallback(size_t token) {
34 return std::make_shared<RecurrentTimer::Callback>([this, token] {
35 std::scoped_lock<std::mutex> lockGuard(mLock);
36
37 mCallbacks.push_back(token);
38 });
39 }
40
getCalledCallbacks()41 std::vector<size_t> getCalledCallbacks() {
42 std::scoped_lock<std::mutex> lockGuard(mLock);
43 return mCallbacks;
44 }
45
clearCalledCallbacks()46 void clearCalledCallbacks() {
47 std::scoped_lock<std::mutex> lockGuard(mLock);
48 mCallbacks.clear();
49 }
50
countTimerCallbackQueue(RecurrentTimer * timer)51 size_t countTimerCallbackQueue(RecurrentTimer* timer) {
52 std::scoped_lock<std::mutex> lockGuard(timer->mLock);
53 return timer->mCallbackQueue.size();
54 }
55
56 private:
57 std::mutex mLock;
58 std::vector<size_t> mCallbacks GUARDED_BY(mLock);
59 };
60
TEST_F(RecurrentTimerTest,testRegisterCallback)61 TEST_F(RecurrentTimerTest, testRegisterCallback) {
62 RecurrentTimer timer;
63 // 0.1s
64 int64_t interval = 100000000;
65
66 auto action = getCallback(0);
67 timer.registerTimerCallback(interval, action);
68
69 std::this_thread::sleep_for(std::chrono::seconds(1));
70
71 timer.unregisterTimerCallback(action);
72
73 // Theoretically trigger 10 times, but check for at least 9 times to be stable.
74 ASSERT_GE(getCalledCallbacks().size(), static_cast<size_t>(9));
75 }
76
TEST_F(RecurrentTimerTest,testRegisterUnregisterRegister)77 TEST_F(RecurrentTimerTest, testRegisterUnregisterRegister) {
78 RecurrentTimer timer;
79 // 0.1s
80 int64_t interval = 100000000;
81
82 auto action = getCallback(0);
83 timer.registerTimerCallback(interval, action);
84
85 std::this_thread::sleep_for(std::chrono::milliseconds(200));
86
87 timer.unregisterTimerCallback(action);
88
89 std::this_thread::sleep_for(std::chrono::milliseconds(200));
90
91 clearCalledCallbacks();
92
93 timer.registerTimerCallback(interval, action);
94
95 std::this_thread::sleep_for(std::chrono::seconds(1));
96
97 // Theoretically trigger 10 times, but check for at least 9 times to be stable.
98 ASSERT_GE(getCalledCallbacks().size(), static_cast<size_t>(9));
99 }
100
TEST_F(RecurrentTimerTest,testDestroyTimerWithCallback)101 TEST_F(RecurrentTimerTest, testDestroyTimerWithCallback) {
102 std::unique_ptr<RecurrentTimer> timer = std::make_unique<RecurrentTimer>();
103 // 0.1s
104 int64_t interval = 100000000;
105
106 auto action = getCallback(0);
107 timer->registerTimerCallback(interval, action);
108
109 std::this_thread::sleep_for(std::chrono::milliseconds(200));
110
111 timer.reset();
112
113 clearCalledCallbacks();
114
115 std::this_thread::sleep_for(std::chrono::milliseconds(200));
116
117 ASSERT_TRUE(getCalledCallbacks().empty());
118 }
119
TEST_F(RecurrentTimerTest,testRegisterMultipleCallbacks)120 TEST_F(RecurrentTimerTest, testRegisterMultipleCallbacks) {
121 RecurrentTimer timer;
122 // 0.1s
123 int64_t interval1 = 100000000;
124 auto action1 = getCallback(1);
125 timer.registerTimerCallback(interval1, action1);
126 // 0.05s
127 int64_t interval2 = 50000000;
128 auto action2 = getCallback(2);
129 timer.registerTimerCallback(interval2, action2);
130 // 0.03s
131 int64_t interval3 = 30000000;
132 auto action3 = getCallback(3);
133 timer.registerTimerCallback(interval3, action3);
134
135 std::this_thread::sleep_for(std::chrono::seconds(1));
136
137 timer.unregisterTimerCallback(action1);
138 timer.unregisterTimerCallback(action2);
139 timer.unregisterTimerCallback(action3);
140
141 size_t action1Count = 0;
142 size_t action2Count = 0;
143 size_t action3Count = 0;
144 for (size_t token : getCalledCallbacks()) {
145 if (token == 1) {
146 action1Count++;
147 }
148 if (token == 2) {
149 action2Count++;
150 }
151 if (token == 3) {
152 action3Count++;
153 }
154 }
155 // Theoretically trigger 10 times, but check for at least 9 times to be stable.
156 ASSERT_GE(action1Count, static_cast<size_t>(9));
157 // Theoretically trigger 20 times, but check for at least 15 times to be stable.
158 ASSERT_GE(action2Count, static_cast<size_t>(15));
159 // Theoretically trigger 33 times, but check for at least 25 times to be stable.
160 ASSERT_GE(action3Count, static_cast<size_t>(25));
161 }
162
TEST_F(RecurrentTimerTest,testRegisterSameCallbackMultipleTimes)163 TEST_F(RecurrentTimerTest, testRegisterSameCallbackMultipleTimes) {
164 RecurrentTimer timer;
165 // 0.02s
166 int64_t interval1 = 20000000;
167 // 0.01s
168 int64_t interval2 = 10000000;
169
170 auto action = getCallback(0);
171 for (int i = 0; i < 10; i++) {
172 timer.registerTimerCallback(interval1, action);
173 timer.registerTimerCallback(interval2, action);
174 }
175
176 clearCalledCallbacks();
177
178 std::this_thread::sleep_for(std::chrono::milliseconds(100));
179
180 // Theoretically trigger 10 times, but check for at least 9 times to be stable.
181 ASSERT_GE(getCalledCallbacks().size(), static_cast<size_t>(9));
182
183 timer.unregisterTimerCallback(action);
184
185 // Make sure there is no item in the callback queue.
186 ASSERT_EQ(countTimerCallbackQueue(&timer), static_cast<size_t>(0));
187 }
188
TEST_F(RecurrentTimerTest,testRegisterCallbackMultipleTimesNoDeadLock)189 TEST_F(RecurrentTimerTest, testRegisterCallbackMultipleTimesNoDeadLock) {
190 // We want to avoid the following situation:
191 // Caller holds a lock while calling registerTimerCallback, registerTimerCallback will try
192 // to obtain an internal lock inside timer.
193 // Meanwhile an recurrent action happens with timer holding an internal lock. The action
194 // tries to obtain the lock currently hold by the caller.
195 // The solution is that while calling recurrent actions, timer must not hold the internal lock.
196
197 std::unique_ptr<RecurrentTimer> timer = std::make_unique<RecurrentTimer>();
198 std::mutex lock;
199 for (size_t i = 0; i < 1000; i++) {
200 std::scoped_lock<std::mutex> lockGuard(lock);
201 auto action = std::make_shared<RecurrentTimer::Callback>([&lock] {
202 // While calling this function, the timer must not hold lock in order not to dead
203 // lock.
204 std::scoped_lock<std::mutex> lockGuard(lock);
205 });
206 // 10ms
207 int64_t interval = 10'000'000;
208 timer->registerTimerCallback(interval, action);
209 // Sleep for a little while to let the recurrent actions begin.
210 std::this_thread::sleep_for(std::chrono::milliseconds(1));
211 }
212 // Make sure we stop the timer before we destroy lock.
213 timer.reset();
214 }
215
216 } // namespace vehicle
217 } // namespace automotive
218 } // namespace hardware
219 } // namespace android
220