1*ec779b8eSAndroid Build Coastguard Worker /*
2*ec779b8eSAndroid Build Coastguard Worker * Copyright (C) 2021 The Android Open Source Project
3*ec779b8eSAndroid Build Coastguard Worker *
4*ec779b8eSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*ec779b8eSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*ec779b8eSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*ec779b8eSAndroid Build Coastguard Worker *
8*ec779b8eSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*ec779b8eSAndroid Build Coastguard Worker *
10*ec779b8eSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*ec779b8eSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*ec779b8eSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*ec779b8eSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*ec779b8eSAndroid Build Coastguard Worker * limitations under the License.
15*ec779b8eSAndroid Build Coastguard Worker */
16*ec779b8eSAndroid Build Coastguard Worker
17*ec779b8eSAndroid Build Coastguard Worker #define LOG_TAG "TimerThread"
18*ec779b8eSAndroid Build Coastguard Worker
19*ec779b8eSAndroid Build Coastguard Worker #include <optional>
20*ec779b8eSAndroid Build Coastguard Worker #include <sstream>
21*ec779b8eSAndroid Build Coastguard Worker #include <unistd.h>
22*ec779b8eSAndroid Build Coastguard Worker #include <vector>
23*ec779b8eSAndroid Build Coastguard Worker
24*ec779b8eSAndroid Build Coastguard Worker #include <audio_utils/mutex.h>
25*ec779b8eSAndroid Build Coastguard Worker #include <mediautils/MediaUtilsDelayed.h>
26*ec779b8eSAndroid Build Coastguard Worker #include <mediautils/TidWrapper.h>
27*ec779b8eSAndroid Build Coastguard Worker #include <mediautils/TimerThread.h>
28*ec779b8eSAndroid Build Coastguard Worker #include <utils/Log.h>
29*ec779b8eSAndroid Build Coastguard Worker #include <utils/ThreadDefs.h>
30*ec779b8eSAndroid Build Coastguard Worker
31*ec779b8eSAndroid Build Coastguard Worker using namespace std::chrono_literals;
32*ec779b8eSAndroid Build Coastguard Worker
33*ec779b8eSAndroid Build Coastguard Worker namespace android::mediautils {
34*ec779b8eSAndroid Build Coastguard Worker
35*ec779b8eSAndroid Build Coastguard Worker extern std::string formatTime(std::chrono::system_clock::time_point t);
36*ec779b8eSAndroid Build Coastguard Worker extern std::string_view timeSuffix(std::string_view time1, std::string_view time2);
37*ec779b8eSAndroid Build Coastguard Worker
scheduleTask(std::string_view tag,TimerCallback && func,Duration timeoutDuration,Duration secondChanceDuration)38*ec779b8eSAndroid Build Coastguard Worker TimerThread::Handle TimerThread::scheduleTask(
39*ec779b8eSAndroid Build Coastguard Worker std::string_view tag, TimerCallback&& func,
40*ec779b8eSAndroid Build Coastguard Worker Duration timeoutDuration, Duration secondChanceDuration) {
41*ec779b8eSAndroid Build Coastguard Worker const auto now = std::chrono::system_clock::now();
42*ec779b8eSAndroid Build Coastguard Worker auto request = std::make_shared<const Request>(now, now +
43*ec779b8eSAndroid Build Coastguard Worker std::chrono::duration_cast<std::chrono::system_clock::duration>(timeoutDuration),
44*ec779b8eSAndroid Build Coastguard Worker secondChanceDuration, getThreadIdWrapper(), tag);
45*ec779b8eSAndroid Build Coastguard Worker return mMonitorThread.add(std::move(request), std::move(func), timeoutDuration);
46*ec779b8eSAndroid Build Coastguard Worker }
47*ec779b8eSAndroid Build Coastguard Worker
trackTask(std::string_view tag)48*ec779b8eSAndroid Build Coastguard Worker TimerThread::Handle TimerThread::trackTask(std::string_view tag) {
49*ec779b8eSAndroid Build Coastguard Worker const auto now = std::chrono::system_clock::now();
50*ec779b8eSAndroid Build Coastguard Worker auto request = std::make_shared<const Request>(now, now,
51*ec779b8eSAndroid Build Coastguard Worker Duration{} /* secondChanceDuration */, getThreadIdWrapper(), tag);
52*ec779b8eSAndroid Build Coastguard Worker return mNoTimeoutMap.add(std::move(request));
53*ec779b8eSAndroid Build Coastguard Worker }
54*ec779b8eSAndroid Build Coastguard Worker
cancelTask(Handle handle)55*ec779b8eSAndroid Build Coastguard Worker bool TimerThread::cancelTask(Handle handle) {
56*ec779b8eSAndroid Build Coastguard Worker std::shared_ptr<const Request> request = isNoTimeoutHandle(handle) ?
57*ec779b8eSAndroid Build Coastguard Worker mNoTimeoutMap.remove(handle) : mMonitorThread.remove(handle);
58*ec779b8eSAndroid Build Coastguard Worker if (!request) return false;
59*ec779b8eSAndroid Build Coastguard Worker mRetiredQueue.add(std::move(request));
60*ec779b8eSAndroid Build Coastguard Worker return true;
61*ec779b8eSAndroid Build Coastguard Worker }
62*ec779b8eSAndroid Build Coastguard Worker
toString(bool showTimeoutStack) const63*ec779b8eSAndroid Build Coastguard Worker std::string TimerThread::SnapshotAnalysis::toString(bool showTimeoutStack) const {
64*ec779b8eSAndroid Build Coastguard Worker // Note: These request queues are snapshot very close together but
65*ec779b8eSAndroid Build Coastguard Worker // not at "identical" times as we don't use a class-wide lock.
66*ec779b8eSAndroid Build Coastguard Worker std::string analysisSummary = std::string("\nanalysis [ ").append(description).append(" ]");
67*ec779b8eSAndroid Build Coastguard Worker std::string timeoutStack;
68*ec779b8eSAndroid Build Coastguard Worker std::string blockedStack;
69*ec779b8eSAndroid Build Coastguard Worker std::string mutexWaitChainStack;
70*ec779b8eSAndroid Build Coastguard Worker if (showTimeoutStack && timeoutTid != -1) {
71*ec779b8eSAndroid Build Coastguard Worker timeoutStack = std::string(suspectTid == timeoutTid ? "\ntimeout/blocked(" : "\ntimeout(")
72*ec779b8eSAndroid Build Coastguard Worker .append(std::to_string(timeoutTid)).append(") callstack [\n")
73*ec779b8eSAndroid Build Coastguard Worker .append(getCallStackStringForTid(timeoutTid)).append("]");
74*ec779b8eSAndroid Build Coastguard Worker }
75*ec779b8eSAndroid Build Coastguard Worker
76*ec779b8eSAndroid Build Coastguard Worker if (suspectTid != -1 && suspectTid != timeoutTid) {
77*ec779b8eSAndroid Build Coastguard Worker blockedStack = std::string("\nblocked(")
78*ec779b8eSAndroid Build Coastguard Worker .append(std::to_string(suspectTid)).append(") callstack [\n")
79*ec779b8eSAndroid Build Coastguard Worker .append(getCallStackStringForTid(suspectTid)).append("]");
80*ec779b8eSAndroid Build Coastguard Worker }
81*ec779b8eSAndroid Build Coastguard Worker
82*ec779b8eSAndroid Build Coastguard Worker if (!mutexWaitChain.empty()) {
83*ec779b8eSAndroid Build Coastguard Worker mutexWaitChainStack.append("\nmutex wait chain [\n");
84*ec779b8eSAndroid Build Coastguard Worker // the wait chain omits the initial timeout tid (which is good as we don't
85*ec779b8eSAndroid Build Coastguard Worker // need to suppress it).
86*ec779b8eSAndroid Build Coastguard Worker for (size_t i = 0; i < mutexWaitChain.size(); ++i) {
87*ec779b8eSAndroid Build Coastguard Worker const auto& [tid, name] = mutexWaitChain[i];
88*ec779b8eSAndroid Build Coastguard Worker mutexWaitChainStack.append("{ tid: ").append(std::to_string(tid))
89*ec779b8eSAndroid Build Coastguard Worker .append(" (holding ").append(name).append(")");
90*ec779b8eSAndroid Build Coastguard Worker if (tid == timeoutTid) {
91*ec779b8eSAndroid Build Coastguard Worker mutexWaitChainStack.append(" TIMEOUT_STACK }\n");
92*ec779b8eSAndroid Build Coastguard Worker } else if (tid == suspectTid) {
93*ec779b8eSAndroid Build Coastguard Worker mutexWaitChainStack.append(" BLOCKED_STACK }\n");
94*ec779b8eSAndroid Build Coastguard Worker } else if (hasMutexCycle && i == mutexWaitChain.size() - 1) {
95*ec779b8eSAndroid Build Coastguard Worker // for a cycle, the last pid in the chain is repeated.
96*ec779b8eSAndroid Build Coastguard Worker mutexWaitChainStack.append(" CYCLE_STACK }\n");
97*ec779b8eSAndroid Build Coastguard Worker } else {
98*ec779b8eSAndroid Build Coastguard Worker mutexWaitChainStack.append(" callstack [\n")
99*ec779b8eSAndroid Build Coastguard Worker .append(getCallStackStringForTid(tid)).append("] }\n");
100*ec779b8eSAndroid Build Coastguard Worker }
101*ec779b8eSAndroid Build Coastguard Worker }
102*ec779b8eSAndroid Build Coastguard Worker mutexWaitChainStack.append("]");
103*ec779b8eSAndroid Build Coastguard Worker }
104*ec779b8eSAndroid Build Coastguard Worker
105*ec779b8eSAndroid Build Coastguard Worker return std::string("now ")
106*ec779b8eSAndroid Build Coastguard Worker .append(formatTime(std::chrono::system_clock::now()))
107*ec779b8eSAndroid Build Coastguard Worker .append("\nsecondChanceCount ")
108*ec779b8eSAndroid Build Coastguard Worker .append(std::to_string(secondChanceCount))
109*ec779b8eSAndroid Build Coastguard Worker .append(analysisSummary)
110*ec779b8eSAndroid Build Coastguard Worker .append("\ntimeout [ ")
111*ec779b8eSAndroid Build Coastguard Worker .append(requestsToString(timeoutRequests))
112*ec779b8eSAndroid Build Coastguard Worker .append(" ]\npending [ ")
113*ec779b8eSAndroid Build Coastguard Worker .append(requestsToString(pendingRequests))
114*ec779b8eSAndroid Build Coastguard Worker .append(" ]\nretired [ ")
115*ec779b8eSAndroid Build Coastguard Worker .append(requestsToString(retiredRequests))
116*ec779b8eSAndroid Build Coastguard Worker .append(" ]")
117*ec779b8eSAndroid Build Coastguard Worker .append(timeoutStack)
118*ec779b8eSAndroid Build Coastguard Worker .append(blockedStack)
119*ec779b8eSAndroid Build Coastguard Worker .append(mutexWaitChainStack);
120*ec779b8eSAndroid Build Coastguard Worker }
121*ec779b8eSAndroid Build Coastguard Worker
122*ec779b8eSAndroid Build Coastguard Worker // A HAL method is where the substring "Hidl" is in the class name.
123*ec779b8eSAndroid Build Coastguard Worker // The tag should look like: ... Hidl ... :: ...
124*ec779b8eSAndroid Build Coastguard Worker // When the audio HAL is updated to AIDL perhaps we will use instead
125*ec779b8eSAndroid Build Coastguard Worker // a global directory of HAL classes.
126*ec779b8eSAndroid Build Coastguard Worker //
127*ec779b8eSAndroid Build Coastguard Worker // See MethodStatistics.cpp:
128*ec779b8eSAndroid Build Coastguard Worker // mediautils::getStatisticsClassesForModule(METHOD_STATISTICS_MODULE_NAME_AUDIO_HIDL)
129*ec779b8eSAndroid Build Coastguard Worker //
130*ec779b8eSAndroid Build Coastguard Worker /* static */
isRequestFromHal(const std::shared_ptr<const Request> & request)131*ec779b8eSAndroid Build Coastguard Worker bool TimerThread::isRequestFromHal(const std::shared_ptr<const Request>& request) {
132*ec779b8eSAndroid Build Coastguard Worker for (const auto& s : {"Hidl", "Aidl"}) {
133*ec779b8eSAndroid Build Coastguard Worker const auto& tagSV = request->tag.asStringView();
134*ec779b8eSAndroid Build Coastguard Worker const size_t halStrPos = tagSV.find(s);
135*ec779b8eSAndroid Build Coastguard Worker // should be a separator afterwards Hidl/Aidl which indicates the string was in the class.
136*ec779b8eSAndroid Build Coastguard Worker if (halStrPos != std::string::npos && tagSV.find("::", halStrPos) != std::string::npos) {
137*ec779b8eSAndroid Build Coastguard Worker return true;
138*ec779b8eSAndroid Build Coastguard Worker }
139*ec779b8eSAndroid Build Coastguard Worker }
140*ec779b8eSAndroid Build Coastguard Worker
141*ec779b8eSAndroid Build Coastguard Worker return false;
142*ec779b8eSAndroid Build Coastguard Worker }
143*ec779b8eSAndroid Build Coastguard Worker
getSnapshotAnalysis(size_t retiredCount) const144*ec779b8eSAndroid Build Coastguard Worker struct TimerThread::SnapshotAnalysis TimerThread::getSnapshotAnalysis(size_t retiredCount) const {
145*ec779b8eSAndroid Build Coastguard Worker struct SnapshotAnalysis analysis{};
146*ec779b8eSAndroid Build Coastguard Worker // The following snapshot of the TimerThread state will be utilized for
147*ec779b8eSAndroid Build Coastguard Worker // analysis. Note, there is no lock around these calls, so there could be
148*ec779b8eSAndroid Build Coastguard Worker // a state update between them.
149*ec779b8eSAndroid Build Coastguard Worker mTimeoutQueue.copyRequests(analysis.timeoutRequests);
150*ec779b8eSAndroid Build Coastguard Worker mRetiredQueue.copyRequests(analysis.retiredRequests, retiredCount);
151*ec779b8eSAndroid Build Coastguard Worker analysis.pendingRequests = getPendingRequests();
152*ec779b8eSAndroid Build Coastguard Worker analysis.secondChanceCount = mMonitorThread.getSecondChanceCount();
153*ec779b8eSAndroid Build Coastguard Worker // No call has timed out, so there is no analysis to be done.
154*ec779b8eSAndroid Build Coastguard Worker if (analysis.timeoutRequests.empty()) {
155*ec779b8eSAndroid Build Coastguard Worker return analysis;
156*ec779b8eSAndroid Build Coastguard Worker }
157*ec779b8eSAndroid Build Coastguard Worker
158*ec779b8eSAndroid Build Coastguard Worker // for now look at last timeout (in our case, the only timeout)
159*ec779b8eSAndroid Build Coastguard Worker const std::shared_ptr<const Request> timeout = analysis.timeoutRequests.back();
160*ec779b8eSAndroid Build Coastguard Worker analysis.timeoutTid = timeout->tid;
161*ec779b8eSAndroid Build Coastguard Worker
162*ec779b8eSAndroid Build Coastguard Worker std::string& description = analysis.description;
163*ec779b8eSAndroid Build Coastguard Worker
164*ec779b8eSAndroid Build Coastguard Worker // audio mutex specific wait chain analysis
165*ec779b8eSAndroid Build Coastguard Worker auto deadlockInfo = audio_utils::mutex::deadlock_detection(analysis.timeoutTid);
166*ec779b8eSAndroid Build Coastguard Worker ALOGD("%s: deadlockInfo: %s", __func__, deadlockInfo.to_string().c_str());
167*ec779b8eSAndroid Build Coastguard Worker
168*ec779b8eSAndroid Build Coastguard Worker if (!deadlockInfo.empty()) {
169*ec779b8eSAndroid Build Coastguard Worker if (!description.empty()) description.append("\n");
170*ec779b8eSAndroid Build Coastguard Worker description.append(deadlockInfo.to_string());
171*ec779b8eSAndroid Build Coastguard Worker }
172*ec779b8eSAndroid Build Coastguard Worker
173*ec779b8eSAndroid Build Coastguard Worker analysis.hasMutexCycle = deadlockInfo.has_cycle;
174*ec779b8eSAndroid Build Coastguard Worker analysis.mutexWaitChain = std::move(deadlockInfo.chain);
175*ec779b8eSAndroid Build Coastguard Worker
176*ec779b8eSAndroid Build Coastguard Worker // no pending requests, we don't attempt to use temporal correlation between a recent call.
177*ec779b8eSAndroid Build Coastguard Worker if (analysis.pendingRequests.empty()) {
178*ec779b8eSAndroid Build Coastguard Worker return analysis;
179*ec779b8eSAndroid Build Coastguard Worker }
180*ec779b8eSAndroid Build Coastguard Worker
181*ec779b8eSAndroid Build Coastguard Worker // pending Requests that are problematic.
182*ec779b8eSAndroid Build Coastguard Worker std::vector<std::shared_ptr<const Request>> pendingExact;
183*ec779b8eSAndroid Build Coastguard Worker std::vector<std::shared_ptr<const Request>> pendingPossible;
184*ec779b8eSAndroid Build Coastguard Worker
185*ec779b8eSAndroid Build Coastguard Worker // We look at pending requests that were scheduled no later than kPendingDuration
186*ec779b8eSAndroid Build Coastguard Worker // after the timeout request. This prevents false matches with calls
187*ec779b8eSAndroid Build Coastguard Worker // that naturally block for a short period of time
188*ec779b8eSAndroid Build Coastguard Worker // such as HAL write() and read().
189*ec779b8eSAndroid Build Coastguard Worker //
190*ec779b8eSAndroid Build Coastguard Worker constexpr Duration kPendingDuration = 1000ms;
191*ec779b8eSAndroid Build Coastguard Worker for (const auto& pending : analysis.pendingRequests) {
192*ec779b8eSAndroid Build Coastguard Worker // If the pending tid is the same as timeout tid, problem identified.
193*ec779b8eSAndroid Build Coastguard Worker if (pending->tid == timeout->tid) {
194*ec779b8eSAndroid Build Coastguard Worker pendingExact.emplace_back(pending);
195*ec779b8eSAndroid Build Coastguard Worker continue;
196*ec779b8eSAndroid Build Coastguard Worker }
197*ec779b8eSAndroid Build Coastguard Worker
198*ec779b8eSAndroid Build Coastguard Worker // if the pending tid is scheduled within time limit
199*ec779b8eSAndroid Build Coastguard Worker if (pending->scheduled - timeout->scheduled < kPendingDuration) {
200*ec779b8eSAndroid Build Coastguard Worker pendingPossible.emplace_back(pending);
201*ec779b8eSAndroid Build Coastguard Worker }
202*ec779b8eSAndroid Build Coastguard Worker }
203*ec779b8eSAndroid Build Coastguard Worker
204*ec779b8eSAndroid Build Coastguard Worker if (!pendingExact.empty()) {
205*ec779b8eSAndroid Build Coastguard Worker const auto& request = pendingExact.front();
206*ec779b8eSAndroid Build Coastguard Worker const bool hal = isRequestFromHal(request);
207*ec779b8eSAndroid Build Coastguard Worker
208*ec779b8eSAndroid Build Coastguard Worker if (hal) {
209*ec779b8eSAndroid Build Coastguard Worker if (!description.empty()) description.append("\n");
210*ec779b8eSAndroid Build Coastguard Worker description.append("Blocked directly due to HAL call: ")
211*ec779b8eSAndroid Build Coastguard Worker .append(request->toString());
212*ec779b8eSAndroid Build Coastguard Worker analysis.suspectTid = request->tid;
213*ec779b8eSAndroid Build Coastguard Worker }
214*ec779b8eSAndroid Build Coastguard Worker }
215*ec779b8eSAndroid Build Coastguard Worker if (description.empty() && !pendingPossible.empty()) {
216*ec779b8eSAndroid Build Coastguard Worker for (const auto& request : pendingPossible) {
217*ec779b8eSAndroid Build Coastguard Worker const bool hal = isRequestFromHal(request);
218*ec779b8eSAndroid Build Coastguard Worker if (hal) {
219*ec779b8eSAndroid Build Coastguard Worker // The first blocked call is the most likely one.
220*ec779b8eSAndroid Build Coastguard Worker // Recent calls might be temporarily blocked
221*ec779b8eSAndroid Build Coastguard Worker // calls such as write() or read() depending on kDuration.
222*ec779b8eSAndroid Build Coastguard Worker description = std::string("Blocked possibly due to HAL call: ")
223*ec779b8eSAndroid Build Coastguard Worker .append(request->toString());
224*ec779b8eSAndroid Build Coastguard Worker analysis.suspectTid= request->tid;
225*ec779b8eSAndroid Build Coastguard Worker }
226*ec779b8eSAndroid Build Coastguard Worker }
227*ec779b8eSAndroid Build Coastguard Worker }
228*ec779b8eSAndroid Build Coastguard Worker return analysis;
229*ec779b8eSAndroid Build Coastguard Worker }
230*ec779b8eSAndroid Build Coastguard Worker
getPendingRequests() const231*ec779b8eSAndroid Build Coastguard Worker std::vector<std::shared_ptr<const TimerThread::Request>> TimerThread::getPendingRequests() const {
232*ec779b8eSAndroid Build Coastguard Worker constexpr size_t kEstimatedPendingRequests = 8; // approx 128 byte alloc.
233*ec779b8eSAndroid Build Coastguard Worker std::vector<std::shared_ptr<const Request>> pendingRequests;
234*ec779b8eSAndroid Build Coastguard Worker pendingRequests.reserve(kEstimatedPendingRequests); // preallocate vector out of lock.
235*ec779b8eSAndroid Build Coastguard Worker
236*ec779b8eSAndroid Build Coastguard Worker // following are internally locked calls, which add to our local pendingRequests.
237*ec779b8eSAndroid Build Coastguard Worker mMonitorThread.copyRequests(pendingRequests);
238*ec779b8eSAndroid Build Coastguard Worker mNoTimeoutMap.copyRequests(pendingRequests);
239*ec779b8eSAndroid Build Coastguard Worker
240*ec779b8eSAndroid Build Coastguard Worker // Sort in order of scheduled time.
241*ec779b8eSAndroid Build Coastguard Worker std::sort(pendingRequests.begin(), pendingRequests.end(),
242*ec779b8eSAndroid Build Coastguard Worker [](const std::shared_ptr<const Request>& r1,
243*ec779b8eSAndroid Build Coastguard Worker const std::shared_ptr<const Request>& r2) {
244*ec779b8eSAndroid Build Coastguard Worker return r1->scheduled < r2->scheduled;
245*ec779b8eSAndroid Build Coastguard Worker });
246*ec779b8eSAndroid Build Coastguard Worker return pendingRequests;
247*ec779b8eSAndroid Build Coastguard Worker }
248*ec779b8eSAndroid Build Coastguard Worker
pendingToString() const249*ec779b8eSAndroid Build Coastguard Worker std::string TimerThread::pendingToString() const {
250*ec779b8eSAndroid Build Coastguard Worker return requestsToString(getPendingRequests());
251*ec779b8eSAndroid Build Coastguard Worker }
252*ec779b8eSAndroid Build Coastguard Worker
retiredToString(size_t n) const253*ec779b8eSAndroid Build Coastguard Worker std::string TimerThread::retiredToString(size_t n) const {
254*ec779b8eSAndroid Build Coastguard Worker std::vector<std::shared_ptr<const Request>> retiredRequests;
255*ec779b8eSAndroid Build Coastguard Worker mRetiredQueue.copyRequests(retiredRequests, n);
256*ec779b8eSAndroid Build Coastguard Worker
257*ec779b8eSAndroid Build Coastguard Worker // Dump to string
258*ec779b8eSAndroid Build Coastguard Worker return requestsToString(retiredRequests);
259*ec779b8eSAndroid Build Coastguard Worker }
260*ec779b8eSAndroid Build Coastguard Worker
timeoutToString(size_t n) const261*ec779b8eSAndroid Build Coastguard Worker std::string TimerThread::timeoutToString(size_t n) const {
262*ec779b8eSAndroid Build Coastguard Worker std::vector<std::shared_ptr<const Request>> timeoutRequests;
263*ec779b8eSAndroid Build Coastguard Worker mTimeoutQueue.copyRequests(timeoutRequests, n);
264*ec779b8eSAndroid Build Coastguard Worker
265*ec779b8eSAndroid Build Coastguard Worker // Dump to string
266*ec779b8eSAndroid Build Coastguard Worker return requestsToString(timeoutRequests);
267*ec779b8eSAndroid Build Coastguard Worker }
268*ec779b8eSAndroid Build Coastguard Worker
toString() const269*ec779b8eSAndroid Build Coastguard Worker std::string TimerThread::Request::toString() const {
270*ec779b8eSAndroid Build Coastguard Worker const auto scheduledString = formatTime(scheduled);
271*ec779b8eSAndroid Build Coastguard Worker const auto deadlineString = formatTime(deadline);
272*ec779b8eSAndroid Build Coastguard Worker return std::string(tag)
273*ec779b8eSAndroid Build Coastguard Worker .append(" scheduled ").append(scheduledString)
274*ec779b8eSAndroid Build Coastguard Worker .append(" deadline ").append(timeSuffix(scheduledString, deadlineString))
275*ec779b8eSAndroid Build Coastguard Worker .append(" tid ").append(std::to_string(tid));
276*ec779b8eSAndroid Build Coastguard Worker }
277*ec779b8eSAndroid Build Coastguard Worker
add(std::shared_ptr<const Request> request)278*ec779b8eSAndroid Build Coastguard Worker void TimerThread::RequestQueue::add(std::shared_ptr<const Request> request) {
279*ec779b8eSAndroid Build Coastguard Worker std::lock_guard lg(mRQMutex);
280*ec779b8eSAndroid Build Coastguard Worker mRequestQueue.emplace_back(std::chrono::system_clock::now(), std::move(request));
281*ec779b8eSAndroid Build Coastguard Worker if (mRequestQueue.size() > mRequestQueueMax) {
282*ec779b8eSAndroid Build Coastguard Worker mRequestQueue.pop_front();
283*ec779b8eSAndroid Build Coastguard Worker }
284*ec779b8eSAndroid Build Coastguard Worker }
285*ec779b8eSAndroid Build Coastguard Worker
copyRequests(std::vector<std::shared_ptr<const Request>> & requests,size_t n) const286*ec779b8eSAndroid Build Coastguard Worker void TimerThread::RequestQueue::copyRequests(
287*ec779b8eSAndroid Build Coastguard Worker std::vector<std::shared_ptr<const Request>>& requests, size_t n) const {
288*ec779b8eSAndroid Build Coastguard Worker std::lock_guard lg(mRQMutex);
289*ec779b8eSAndroid Build Coastguard Worker const size_t size = mRequestQueue.size();
290*ec779b8eSAndroid Build Coastguard Worker size_t i = n >= size ? 0 : size - n;
291*ec779b8eSAndroid Build Coastguard Worker for (; i < size; ++i) {
292*ec779b8eSAndroid Build Coastguard Worker const auto &[time, request] = mRequestQueue[i];
293*ec779b8eSAndroid Build Coastguard Worker requests.emplace_back(request);
294*ec779b8eSAndroid Build Coastguard Worker }
295*ec779b8eSAndroid Build Coastguard Worker }
296*ec779b8eSAndroid Build Coastguard Worker
add(std::shared_ptr<const Request> request)297*ec779b8eSAndroid Build Coastguard Worker TimerThread::Handle TimerThread::NoTimeoutMap::add(std::shared_ptr<const Request> request) {
298*ec779b8eSAndroid Build Coastguard Worker std::lock_guard lg(mNTMutex);
299*ec779b8eSAndroid Build Coastguard Worker // A unique handle is obtained by mNoTimeoutRequests.fetch_add(1),
300*ec779b8eSAndroid Build Coastguard Worker // This need not be under a lock, but we do so anyhow.
301*ec779b8eSAndroid Build Coastguard Worker const Handle handle = getUniqueHandle_l();
302*ec779b8eSAndroid Build Coastguard Worker mMap[handle] = request;
303*ec779b8eSAndroid Build Coastguard Worker return handle;
304*ec779b8eSAndroid Build Coastguard Worker }
305*ec779b8eSAndroid Build Coastguard Worker
remove(Handle handle)306*ec779b8eSAndroid Build Coastguard Worker std::shared_ptr<const TimerThread::Request> TimerThread::NoTimeoutMap::remove(Handle handle) {
307*ec779b8eSAndroid Build Coastguard Worker std::lock_guard lg(mNTMutex);
308*ec779b8eSAndroid Build Coastguard Worker auto it = mMap.find(handle);
309*ec779b8eSAndroid Build Coastguard Worker if (it == mMap.end()) return {};
310*ec779b8eSAndroid Build Coastguard Worker auto request = it->second;
311*ec779b8eSAndroid Build Coastguard Worker mMap.erase(it);
312*ec779b8eSAndroid Build Coastguard Worker return request;
313*ec779b8eSAndroid Build Coastguard Worker }
314*ec779b8eSAndroid Build Coastguard Worker
copyRequests(std::vector<std::shared_ptr<const Request>> & requests) const315*ec779b8eSAndroid Build Coastguard Worker void TimerThread::NoTimeoutMap::copyRequests(
316*ec779b8eSAndroid Build Coastguard Worker std::vector<std::shared_ptr<const Request>>& requests) const {
317*ec779b8eSAndroid Build Coastguard Worker std::lock_guard lg(mNTMutex);
318*ec779b8eSAndroid Build Coastguard Worker for (const auto &[handle, request] : mMap) {
319*ec779b8eSAndroid Build Coastguard Worker requests.emplace_back(request);
320*ec779b8eSAndroid Build Coastguard Worker }
321*ec779b8eSAndroid Build Coastguard Worker }
322*ec779b8eSAndroid Build Coastguard Worker
MonitorThread(RequestQueue & timeoutQueue)323*ec779b8eSAndroid Build Coastguard Worker TimerThread::MonitorThread::MonitorThread(RequestQueue& timeoutQueue)
324*ec779b8eSAndroid Build Coastguard Worker : mTimeoutQueue(timeoutQueue)
325*ec779b8eSAndroid Build Coastguard Worker , mThread([this] { threadFunc(); }) {
326*ec779b8eSAndroid Build Coastguard Worker pthread_setname_np(mThread.native_handle(), "TimerThread");
327*ec779b8eSAndroid Build Coastguard Worker pthread_setschedprio(mThread.native_handle(), PRIORITY_URGENT_AUDIO);
328*ec779b8eSAndroid Build Coastguard Worker }
329*ec779b8eSAndroid Build Coastguard Worker
~MonitorThread()330*ec779b8eSAndroid Build Coastguard Worker TimerThread::MonitorThread::~MonitorThread() {
331*ec779b8eSAndroid Build Coastguard Worker {
332*ec779b8eSAndroid Build Coastguard Worker std::lock_guard _l(mMutex);
333*ec779b8eSAndroid Build Coastguard Worker mShouldExit = true;
334*ec779b8eSAndroid Build Coastguard Worker mCond.notify_all();
335*ec779b8eSAndroid Build Coastguard Worker }
336*ec779b8eSAndroid Build Coastguard Worker mThread.join();
337*ec779b8eSAndroid Build Coastguard Worker }
338*ec779b8eSAndroid Build Coastguard Worker
threadFunc()339*ec779b8eSAndroid Build Coastguard Worker void TimerThread::MonitorThread::threadFunc() {
340*ec779b8eSAndroid Build Coastguard Worker std::unique_lock _l(mMutex);
341*ec779b8eSAndroid Build Coastguard Worker ::android::base::ScopedLockAssertion lock_assertion(mMutex);
342*ec779b8eSAndroid Build Coastguard Worker while (!mShouldExit) {
343*ec779b8eSAndroid Build Coastguard Worker Handle nextDeadline = INVALID_HANDLE;
344*ec779b8eSAndroid Build Coastguard Worker Handle now = INVALID_HANDLE;
345*ec779b8eSAndroid Build Coastguard Worker if (!mMonitorRequests.empty()) {
346*ec779b8eSAndroid Build Coastguard Worker nextDeadline = mMonitorRequests.begin()->first;
347*ec779b8eSAndroid Build Coastguard Worker now = std::chrono::steady_clock::now();
348*ec779b8eSAndroid Build Coastguard Worker if (nextDeadline < now) {
349*ec779b8eSAndroid Build Coastguard Worker auto node = mMonitorRequests.extract(mMonitorRequests.begin());
350*ec779b8eSAndroid Build Coastguard Worker // Deadline has expired, handle the request.
351*ec779b8eSAndroid Build Coastguard Worker auto secondChanceDuration = node.mapped().first->secondChanceDuration;
352*ec779b8eSAndroid Build Coastguard Worker if (secondChanceDuration.count() != 0) {
353*ec779b8eSAndroid Build Coastguard Worker // We now apply the second chance duration to find the clock
354*ec779b8eSAndroid Build Coastguard Worker // monotonic second deadline. The unique key is then the
355*ec779b8eSAndroid Build Coastguard Worker // pair<second_deadline, first_deadline>.
356*ec779b8eSAndroid Build Coastguard Worker //
357*ec779b8eSAndroid Build Coastguard Worker // The second chance prevents a false timeout should there be
358*ec779b8eSAndroid Build Coastguard Worker // any clock monotonic advancement during suspend.
359*ec779b8eSAndroid Build Coastguard Worker auto newHandle = now + secondChanceDuration;
360*ec779b8eSAndroid Build Coastguard Worker ALOGD("%s: TimeCheck second chance applied for %s",
361*ec779b8eSAndroid Build Coastguard Worker __func__, node.mapped().first->tag.c_str()); // should be rare event.
362*ec779b8eSAndroid Build Coastguard Worker mSecondChanceRequests.emplace_hint(mSecondChanceRequests.end(),
363*ec779b8eSAndroid Build Coastguard Worker std::make_pair(newHandle, nextDeadline),
364*ec779b8eSAndroid Build Coastguard Worker std::move(node.mapped()));
365*ec779b8eSAndroid Build Coastguard Worker // increment second chance counter.
366*ec779b8eSAndroid Build Coastguard Worker mSecondChanceCount.fetch_add(1 /* arg */, std::memory_order_relaxed);
367*ec779b8eSAndroid Build Coastguard Worker } else {
368*ec779b8eSAndroid Build Coastguard Worker {
369*ec779b8eSAndroid Build Coastguard Worker _l.unlock();
370*ec779b8eSAndroid Build Coastguard Worker // We add Request to retired queue early so that it can be dumped out.
371*ec779b8eSAndroid Build Coastguard Worker mTimeoutQueue.add(std::move(node.mapped().first));
372*ec779b8eSAndroid Build Coastguard Worker node.mapped().second(nextDeadline);
373*ec779b8eSAndroid Build Coastguard Worker // Caution: we don't hold lock when we call TimerCallback,
374*ec779b8eSAndroid Build Coastguard Worker // but this is the timeout case! We will crash soon,
375*ec779b8eSAndroid Build Coastguard Worker // maybe before returning.
376*ec779b8eSAndroid Build Coastguard Worker // anything left over is released here outside lock.
377*ec779b8eSAndroid Build Coastguard Worker }
378*ec779b8eSAndroid Build Coastguard Worker // reacquire the lock - if something was added, we loop immediately to check.
379*ec779b8eSAndroid Build Coastguard Worker _l.lock();
380*ec779b8eSAndroid Build Coastguard Worker }
381*ec779b8eSAndroid Build Coastguard Worker // always process expiring monitor requests first.
382*ec779b8eSAndroid Build Coastguard Worker continue;
383*ec779b8eSAndroid Build Coastguard Worker }
384*ec779b8eSAndroid Build Coastguard Worker }
385*ec779b8eSAndroid Build Coastguard Worker // now process any second chance requests.
386*ec779b8eSAndroid Build Coastguard Worker if (!mSecondChanceRequests.empty()) {
387*ec779b8eSAndroid Build Coastguard Worker Handle secondDeadline = mSecondChanceRequests.begin()->first.first;
388*ec779b8eSAndroid Build Coastguard Worker if (now == INVALID_HANDLE) now = std::chrono::steady_clock::now();
389*ec779b8eSAndroid Build Coastguard Worker if (secondDeadline < now) {
390*ec779b8eSAndroid Build Coastguard Worker auto node = mSecondChanceRequests.extract(mSecondChanceRequests.begin());
391*ec779b8eSAndroid Build Coastguard Worker {
392*ec779b8eSAndroid Build Coastguard Worker _l.unlock();
393*ec779b8eSAndroid Build Coastguard Worker // We add Request to retired queue early so that it can be dumped out.
394*ec779b8eSAndroid Build Coastguard Worker mTimeoutQueue.add(std::move(node.mapped().first));
395*ec779b8eSAndroid Build Coastguard Worker const Handle originalHandle = node.key().second;
396*ec779b8eSAndroid Build Coastguard Worker node.mapped().second(originalHandle);
397*ec779b8eSAndroid Build Coastguard Worker // Caution: we don't hold lock when we call TimerCallback.
398*ec779b8eSAndroid Build Coastguard Worker // This is benign issue - we permit concurrent operations
399*ec779b8eSAndroid Build Coastguard Worker // while in the callback to the MonitorQueue.
400*ec779b8eSAndroid Build Coastguard Worker //
401*ec779b8eSAndroid Build Coastguard Worker // Anything left over is released here outside lock.
402*ec779b8eSAndroid Build Coastguard Worker }
403*ec779b8eSAndroid Build Coastguard Worker // reacquire the lock - if something was added, we loop immediately to check.
404*ec779b8eSAndroid Build Coastguard Worker _l.lock();
405*ec779b8eSAndroid Build Coastguard Worker continue;
406*ec779b8eSAndroid Build Coastguard Worker }
407*ec779b8eSAndroid Build Coastguard Worker // update the deadline.
408*ec779b8eSAndroid Build Coastguard Worker if (nextDeadline == INVALID_HANDLE) {
409*ec779b8eSAndroid Build Coastguard Worker nextDeadline = secondDeadline;
410*ec779b8eSAndroid Build Coastguard Worker } else {
411*ec779b8eSAndroid Build Coastguard Worker nextDeadline = std::min(nextDeadline, secondDeadline);
412*ec779b8eSAndroid Build Coastguard Worker }
413*ec779b8eSAndroid Build Coastguard Worker }
414*ec779b8eSAndroid Build Coastguard Worker if (nextDeadline != INVALID_HANDLE) {
415*ec779b8eSAndroid Build Coastguard Worker mCond.wait_until(_l, nextDeadline);
416*ec779b8eSAndroid Build Coastguard Worker } else {
417*ec779b8eSAndroid Build Coastguard Worker mCond.wait(_l);
418*ec779b8eSAndroid Build Coastguard Worker }
419*ec779b8eSAndroid Build Coastguard Worker }
420*ec779b8eSAndroid Build Coastguard Worker }
421*ec779b8eSAndroid Build Coastguard Worker
add(std::shared_ptr<const Request> request,TimerCallback && func,Duration timeout)422*ec779b8eSAndroid Build Coastguard Worker TimerThread::Handle TimerThread::MonitorThread::add(
423*ec779b8eSAndroid Build Coastguard Worker std::shared_ptr<const Request> request, TimerCallback&& func, Duration timeout) {
424*ec779b8eSAndroid Build Coastguard Worker std::lock_guard _l(mMutex);
425*ec779b8eSAndroid Build Coastguard Worker const Handle handle = getUniqueHandle_l(timeout);
426*ec779b8eSAndroid Build Coastguard Worker mMonitorRequests.emplace_hint(mMonitorRequests.end(),
427*ec779b8eSAndroid Build Coastguard Worker handle, std::make_pair(std::move(request), std::move(func)));
428*ec779b8eSAndroid Build Coastguard Worker mCond.notify_all();
429*ec779b8eSAndroid Build Coastguard Worker return handle;
430*ec779b8eSAndroid Build Coastguard Worker }
431*ec779b8eSAndroid Build Coastguard Worker
remove(Handle handle)432*ec779b8eSAndroid Build Coastguard Worker std::shared_ptr<const TimerThread::Request> TimerThread::MonitorThread::remove(Handle handle) {
433*ec779b8eSAndroid Build Coastguard Worker std::pair<std::shared_ptr<const Request>, TimerCallback> data;
434*ec779b8eSAndroid Build Coastguard Worker std::unique_lock ul(mMutex);
435*ec779b8eSAndroid Build Coastguard Worker ::android::base::ScopedLockAssertion lock_assertion(mMutex);
436*ec779b8eSAndroid Build Coastguard Worker if (const auto it = mMonitorRequests.find(handle);
437*ec779b8eSAndroid Build Coastguard Worker it != mMonitorRequests.end()) {
438*ec779b8eSAndroid Build Coastguard Worker data = std::move(it->second);
439*ec779b8eSAndroid Build Coastguard Worker mMonitorRequests.erase(it);
440*ec779b8eSAndroid Build Coastguard Worker ul.unlock(); // manually release lock here so func (data.second)
441*ec779b8eSAndroid Build Coastguard Worker // is released outside of lock.
442*ec779b8eSAndroid Build Coastguard Worker return data.first; // request
443*ec779b8eSAndroid Build Coastguard Worker }
444*ec779b8eSAndroid Build Coastguard Worker
445*ec779b8eSAndroid Build Coastguard Worker // this check is O(N), but since the second chance requests are ordered
446*ec779b8eSAndroid Build Coastguard Worker // in terms of earliest expiration time, we would expect better than average results.
447*ec779b8eSAndroid Build Coastguard Worker for (auto it = mSecondChanceRequests.begin(); it != mSecondChanceRequests.end(); ++it) {
448*ec779b8eSAndroid Build Coastguard Worker if (it->first.second == handle) {
449*ec779b8eSAndroid Build Coastguard Worker data = std::move(it->second);
450*ec779b8eSAndroid Build Coastguard Worker mSecondChanceRequests.erase(it);
451*ec779b8eSAndroid Build Coastguard Worker ul.unlock(); // manually release lock here so func (data.second)
452*ec779b8eSAndroid Build Coastguard Worker // is released outside of lock.
453*ec779b8eSAndroid Build Coastguard Worker return data.first; // request
454*ec779b8eSAndroid Build Coastguard Worker }
455*ec779b8eSAndroid Build Coastguard Worker }
456*ec779b8eSAndroid Build Coastguard Worker return {};
457*ec779b8eSAndroid Build Coastguard Worker }
458*ec779b8eSAndroid Build Coastguard Worker
copyRequests(std::vector<std::shared_ptr<const Request>> & requests) const459*ec779b8eSAndroid Build Coastguard Worker void TimerThread::MonitorThread::copyRequests(
460*ec779b8eSAndroid Build Coastguard Worker std::vector<std::shared_ptr<const Request>>& requests) const {
461*ec779b8eSAndroid Build Coastguard Worker std::lock_guard lg(mMutex);
462*ec779b8eSAndroid Build Coastguard Worker for (const auto &[deadline, monitorpair] : mMonitorRequests) {
463*ec779b8eSAndroid Build Coastguard Worker requests.emplace_back(monitorpair.first);
464*ec779b8eSAndroid Build Coastguard Worker }
465*ec779b8eSAndroid Build Coastguard Worker // we combine the second map with the first map - this is
466*ec779b8eSAndroid Build Coastguard Worker // everything that is pending on the monitor thread.
467*ec779b8eSAndroid Build Coastguard Worker // The second map will be older than the first map so this
468*ec779b8eSAndroid Build Coastguard Worker // is in order.
469*ec779b8eSAndroid Build Coastguard Worker for (const auto &[deadline, monitorpair] : mSecondChanceRequests) {
470*ec779b8eSAndroid Build Coastguard Worker requests.emplace_back(monitorpair.first);
471*ec779b8eSAndroid Build Coastguard Worker }
472*ec779b8eSAndroid Build Coastguard Worker }
473*ec779b8eSAndroid Build Coastguard Worker
474*ec779b8eSAndroid Build Coastguard Worker } // namespace android::mediautils
475