xref: /aosp_15_r20/frameworks/av/media/utils/TimerThread.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
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