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 #include <condition_variable>
22 
23 #include <chrono>
24 #include <memory>
25 #include <mutex>
26 
27 namespace android {
28 namespace hardware {
29 namespace automotive {
30 namespace vehicle {
31 
32 using ::android::base::ScopedLockAssertion;
33 
34 class RecurrentTimerTest : public testing::Test {
35   public:
getCallback(size_t token)36     std::shared_ptr<RecurrentTimer::Callback> getCallback(size_t token) {
37         return std::make_shared<RecurrentTimer::Callback>([this, token] {
38             std::scoped_lock<std::mutex> lockGuard(mLock);
39 
40             mCallbacks.push_back(token);
41             mCond.notify_all();
42         });
43     }
44 
waitForCalledCallbacks(size_t count,size_t timeoutInMs)45     bool waitForCalledCallbacks(size_t count, size_t timeoutInMs) {
46         std::unique_lock<std::mutex> uniqueLock(mLock);
47         return mCond.wait_for(uniqueLock, std::chrono::milliseconds(timeoutInMs), [this, count] {
48             ScopedLockAssertion lockAssertion(mLock);
49             return mCallbacks.size() >= count;
50         });
51     }
52 
getCalledCallbacks()53     std::vector<size_t> getCalledCallbacks() {
54         std::scoped_lock<std::mutex> lockGuard(mLock);
55         return mCallbacks;
56     }
57 
clearCalledCallbacks()58     void clearCalledCallbacks() {
59         std::scoped_lock<std::mutex> lockGuard(mLock);
60         mCallbacks.clear();
61     }
62 
countCallbackInfoById(RecurrentTimer * timer)63     size_t countCallbackInfoById(RecurrentTimer* timer) {
64         std::scoped_lock<std::mutex> lockGuard(timer->mLock);
65         return timer->mCallbackInfoById.size();
66     }
67 
countIdByCallback(RecurrentTimer * timer)68     size_t countIdByCallback(RecurrentTimer* timer) {
69         std::scoped_lock<std::mutex> lockGuard(timer->mLock);
70         return timer->mIdByCallback.size();
71     }
72 
73   private:
74     std::condition_variable mCond;
75     std::mutex mLock;
76     std::vector<size_t> mCallbacks GUARDED_BY(mLock);
77 };
78 
TEST_F(RecurrentTimerTest,testRegisterCallback)79 TEST_F(RecurrentTimerTest, testRegisterCallback) {
80     RecurrentTimer timer;
81     // 0.1s
82     int64_t interval = 100000000;
83 
84     auto action = getCallback(0);
85     timer.registerTimerCallback(interval, action);
86 
87     // Should only takes 1s, use 5s as timeout to be safe.
88     ASSERT_TRUE(waitForCalledCallbacks(/* count= */ 10u, /* timeoutInMs= */ 5000))
89             << "Not enough callbacks called before timeout";
90 
91     timer.unregisterTimerCallback(action);
92 }
93 
TEST_F(RecurrentTimerTest,testRegisterUnregisterRegister)94 TEST_F(RecurrentTimerTest, testRegisterUnregisterRegister) {
95     RecurrentTimer timer;
96     // 0.1s
97     int64_t interval = 100000000;
98 
99     auto action = getCallback(0);
100     timer.registerTimerCallback(interval, action);
101 
102     std::this_thread::sleep_for(std::chrono::milliseconds(200));
103 
104     timer.unregisterTimerCallback(action);
105 
106     std::this_thread::sleep_for(std::chrono::milliseconds(200));
107 
108     clearCalledCallbacks();
109 
110     timer.registerTimerCallback(interval, action);
111 
112     // Should only takes 1s, use 5s as timeout to be safe.
113     ASSERT_TRUE(waitForCalledCallbacks(/* count= */ 10u, /* timeoutInMs= */ 5000))
114             << "Not enough callbacks called before timeout";
115 
116     timer.unregisterTimerCallback(action);
117 
118     ASSERT_EQ(countCallbackInfoById(&timer), 0u);
119     ASSERT_EQ(countIdByCallback(&timer), 0u);
120 }
121 
TEST_F(RecurrentTimerTest,testDestroyTimerWithCallback)122 TEST_F(RecurrentTimerTest, testDestroyTimerWithCallback) {
123     std::unique_ptr<RecurrentTimer> timer = std::make_unique<RecurrentTimer>();
124     // 0.1s
125     int64_t interval = 100000000;
126 
127     auto action = getCallback(0);
128     timer->registerTimerCallback(interval, action);
129 
130     std::this_thread::sleep_for(std::chrono::milliseconds(200));
131 
132     timer.reset();
133 
134     clearCalledCallbacks();
135 
136     std::this_thread::sleep_for(std::chrono::milliseconds(200));
137 
138     // Should be 0, but in rare cases there might be 1 events in the queue while the timer is
139     // being destroyed.
140     ASSERT_LE(getCalledCallbacks().size(), 1u);
141 }
142 
TEST_F(RecurrentTimerTest,testRegisterMultipleCallbacks)143 TEST_F(RecurrentTimerTest, testRegisterMultipleCallbacks) {
144     RecurrentTimer timer;
145     // 0.1s
146     int64_t interval1 = 100000000;
147     auto action1 = getCallback(1);
148     timer.registerTimerCallback(interval1, action1);
149     // 0.05s
150     int64_t interval2 = 50000000;
151     auto action2 = getCallback(2);
152     timer.registerTimerCallback(interval2, action2);
153     // 0.03s
154     int64_t interval3 = 30000000;
155     auto action3 = getCallback(3);
156     timer.registerTimerCallback(interval3, action3);
157 
158     // In 1s, we should generate 10 + 20 + 33 = 63 events.
159     // Here we are waiting for more events to make sure we receive enough events for each actions.
160     // Use 5s as timeout to be safe.
161     ASSERT_TRUE(waitForCalledCallbacks(/* count= */ 70u, /* timeoutInMs= */ 5000))
162             << "Not enough callbacks called before timeout";
163 
164     timer.unregisterTimerCallback(action1);
165     timer.unregisterTimerCallback(action2);
166     timer.unregisterTimerCallback(action3);
167 
168     size_t action1Count = 0;
169     size_t action2Count = 0;
170     size_t action3Count = 0;
171     for (size_t token : getCalledCallbacks()) {
172         if (token == 1) {
173             action1Count++;
174         }
175         if (token == 2) {
176             action2Count++;
177         }
178         if (token == 3) {
179             action3Count++;
180         }
181     }
182 
183     ASSERT_GE(action1Count, static_cast<size_t>(10));
184     ASSERT_GE(action2Count, static_cast<size_t>(20));
185     ASSERT_GE(action3Count, static_cast<size_t>(33));
186 }
187 
TEST_F(RecurrentTimerTest,testRegisterSameCallbackMultipleTimes)188 TEST_F(RecurrentTimerTest, testRegisterSameCallbackMultipleTimes) {
189     RecurrentTimer timer;
190     // 0.2s
191     int64_t interval1 = 200'000'000;
192     // 0.1s
193     int64_t interval2 = 100'000'000;
194 
195     auto action = getCallback(0);
196     for (int i = 0; i < 10; i++) {
197         timer.registerTimerCallback(interval1, action);
198         timer.registerTimerCallback(interval2, action);
199     }
200 
201     clearCalledCallbacks();
202 
203     // Should only takes 1s, use 5s as timeout to be safe.
204     ASSERT_TRUE(waitForCalledCallbacks(/* count= */ 10u, /* timeoutInMs= */ 5000))
205             << "Not enough callbacks called before timeout";
206 
207     timer.unregisterTimerCallback(action);
208 
209     ASSERT_EQ(countCallbackInfoById(&timer), 0u);
210     ASSERT_EQ(countIdByCallback(&timer), 0u);
211 }
212 
TEST_F(RecurrentTimerTest,testRegisterCallbackMultipleTimesNoDeadLock)213 TEST_F(RecurrentTimerTest, testRegisterCallbackMultipleTimesNoDeadLock) {
214     // We want to avoid the following situation:
215     // Caller holds a lock while calling registerTimerCallback, registerTimerCallback will try
216     // to obtain an internal lock inside timer.
217     // Meanwhile an recurrent action happens with timer holding an internal lock. The action
218     // tries to obtain the lock currently hold by the caller.
219     // The solution is that while calling recurrent actions, timer must not hold the internal lock.
220 
221     std::unique_ptr<RecurrentTimer> timer = std::make_unique<RecurrentTimer>();
222     std::mutex lock;
223     for (size_t i = 0; i < 1000; i++) {
224         std::scoped_lock<std::mutex> lockGuard(lock);
225         auto action = std::make_shared<RecurrentTimer::Callback>([&lock] {
226             // While calling this function, the timer must not hold lock in order not to dead
227             // lock.
228             std::scoped_lock<std::mutex> lockGuard(lock);
229         });
230         // 10ms
231         int64_t interval = 10'000'000;
232         timer->registerTimerCallback(interval, action);
233         // Sleep for a little while to let the recurrent actions begin.
234         std::this_thread::sleep_for(std::chrono::milliseconds(1));
235     }
236     // Make sure we stop the timer before we destroy lock.
237     timer.reset();
238 }
239 
240 }  // namespace vehicle
241 }  // namespace automotive
242 }  // namespace hardware
243 }  // namespace android
244