1 /*
2 * Copyright 2020 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 #define LOG_TAG "carwatchdogd_ioperf_test"
18
19 #include "LooperStub.h"
20
21 #include <android-base/chrono_utils.h>
22 #include <utils/Looper.h>
23
24 #include <future>
25
26 namespace android {
27 namespace automotive {
28 namespace watchdog {
29 namespace testing {
30
31 using android::base::Error;
32 using android::base::Result;
33
34 // As the messages, which are to be polled immediately, are enqueued in the underlying looper
35 // handler before calling its poll method, the looper handler doesn't have to wait for any new
36 // messages.
37 const std::chrono::milliseconds kLooperPollTimeout = 0ms;
38
39 // Maximum timeout before giving up on the underlying looper handler. This doesn't block the test
40 // as long as the underlying looper handler processes the enqueued messages quickly and updates
41 // |mShouldPoll|.
42 const std::chrono::milliseconds kStubPollCheckTimeout = 3min;
43
pollAll(int)44 int LooperStub::pollAll(int /*timeoutMillis*/) {
45 {
46 Mutex::Autolock lock(mMutex);
47 if (!mShouldPoll) {
48 return 0;
49 }
50 mElapsedTime = mTimer;
51 while (!mCache.empty() && mCache.front().empty()) {
52 mTimer += 1s; // Each empty entry in the cache is a second elapsed.
53 mCache.erase(mCache.begin());
54 }
55 mElapsedTime = mTimer - mElapsedTime;
56 if (mCache.empty()) {
57 mShouldPoll = false;
58 return 0;
59 }
60 // Send messages from the top of the cache and poll them immediately.
61 const auto messages = mCache.front();
62 for (const auto& m : messages) {
63 mLooper->sendMessage(mHandler, m);
64 }
65 mCache.erase(mCache.begin());
66 }
67 int result = mLooper->pollAll(kLooperPollTimeout.count());
68 Mutex::Autolock lock(mMutex);
69 mShouldPoll = false;
70 return result;
71 }
72
sendMessage(const android::sp<MessageHandler> & handler,const Message & message)73 void LooperStub::sendMessage(const android::sp<MessageHandler>& handler, const Message& message) {
74 sendMessageAtTime(now(), handler, message);
75 }
76
sendMessageAtTime(nsecs_t uptime,const android::sp<MessageHandler> & handler,const Message & message)77 void LooperStub::sendMessageAtTime(nsecs_t uptime, const android::sp<MessageHandler>& handler,
78 const Message& message) {
79 Mutex::Autolock lock(mMutex);
80 mHandler = handler;
81 nsecs_t uptimeDelay = uptime - now();
82 size_t pos = static_cast<size_t>(ns2s(uptimeDelay));
83 while (mCache.size() < pos + 1) {
84 mCache.emplace_back(LooperStub::CacheEntry());
85 }
86 mCache[pos].emplace_back(message);
87 }
88
removeMessages(const android::sp<MessageHandler> & handler)89 void LooperStub::removeMessages(const android::sp<MessageHandler>& handler) {
90 Mutex::Autolock lock(mMutex);
91 mCache.clear();
92 mLooper->removeMessages(handler);
93 }
94
pollCache()95 Result<void> LooperStub::pollCache() {
96 {
97 Mutex::Autolock lock(mMutex);
98 mShouldPoll = true;
99 }
100 auto checkPollCompleted = std::async([&]() {
101 bool shouldPoll = true;
102 while (shouldPoll) {
103 Mutex::Autolock lock(mMutex);
104 shouldPoll = mShouldPoll;
105 }
106 });
107 if (checkPollCompleted.wait_for(kStubPollCheckTimeout) != std::future_status::ready) {
108 mShouldPoll = false;
109 return Error() << "Poll didn't complete before " << kStubPollCheckTimeout.count()
110 << " milliseconds";
111 }
112 return {};
113 }
114
115 } // namespace testing
116 } // namespace watchdog
117 } // namespace automotive
118 } // namespace android
119