blob: 8f328ee3c723ecac42121e745e857f882f1f47ea [file] [log] [blame]
Glenn Kastenc15d6652012-05-30 14:52:57 -07001/*
2 * Copyright (C) 2012 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 "AudioWatchdog"
18//#define LOG_NDEBUG 0
19
20#include <utils/Log.h>
21#include "AudioWatchdog.h"
22
23namespace android {
24
25void AudioWatchdogDump::dump(int fd)
26{
27 char buf[32];
28 if (mMostRecent != 0) {
29 // includes NUL terminator
30 ctime_r(&mMostRecent, buf);
31 } else {
32 strcpy(buf, "N/A\n");
33 }
34 fdprintf(fd, "Watchdog: underruns=%u, logs=%u, most recent underrun log at %s",
35 mUnderruns, mLogs, buf);
36}
37
38bool AudioWatchdog::threadLoop()
39{
40 {
41 AutoMutex _l(mMyLock);
42 if (mPaused) {
43 mMyCond.wait(mMyLock);
44 // ignore previous timestamp after resume()
45 mOldTsValid = false;
46 // force an immediate log on first underrun after resume()
47 mLogTs.tv_sec = MIN_TIME_BETWEEN_LOGS_SEC;
48 mLogTs.tv_nsec = 0;
49 // caller will check for exitPending()
50 return true;
51 }
52 }
53 struct timespec newTs;
54 int rc = clock_gettime(CLOCK_MONOTONIC, &newTs);
55 if (rc != 0) {
56 pause();
57 return false;
58 }
59 if (!mOldTsValid) {
60 mOldTs = newTs;
61 mOldTsValid = true;
62 return true;
63 }
64 time_t sec = newTs.tv_sec - mOldTs.tv_sec;
65 long nsec = newTs.tv_nsec - mOldTs.tv_nsec;
66 if (nsec < 0) {
67 --sec;
68 nsec += 1000000000;
69 }
70 mOldTs = newTs;
71 // cycleNs is same as sec*1e9 + nsec, but limited to about 4 seconds
72 uint32_t cycleNs = nsec;
73 if (sec > 0) {
74 if (sec < 4) {
75 cycleNs += sec * 1000000000;
76 } else {
77 cycleNs = 4000000000u;
78 }
79 }
80 mLogTs.tv_sec += sec;
81 if ((mLogTs.tv_nsec += nsec) >= 1000000000) {
82 mLogTs.tv_sec++;
83 mLogTs.tv_nsec -= 1000000000;
84 }
85 if (cycleNs > mMaxCycleNs) {
86 mDump->mUnderruns = ++mUnderruns;
87 if (mLogTs.tv_sec >= MIN_TIME_BETWEEN_LOGS_SEC) {
88 mDump->mLogs = ++mLogs;
89 mDump->mMostRecent = time(NULL);
90 ALOGW("Insufficient CPU for load: expected=%.1f actual=%.1f ms; underruns=%u logs=%u",
91 mPeriodNs * 1e-6, cycleNs * 1e-6, mUnderruns, mLogs);
92 mLogTs.tv_sec = 0;
93 mLogTs.tv_nsec = 0;
94 }
95 }
96 struct timespec req;
97 req.tv_sec = 0;
98 req.tv_nsec = mPeriodNs;
99 rc = nanosleep(&req, NULL);
100 if (!((rc == 0) || (rc == -1 && errno == EINTR))) {
101 pause();
102 return false;
103 }
104 return true;
105}
106
107void AudioWatchdog::requestExit()
108{
109 // must be in this order to avoid a race condition
110 Thread::requestExit();
111 resume();
112}
113
114void AudioWatchdog::pause()
115{
116 AutoMutex _l(mMyLock);
117 mPaused = true;
118}
119
120void AudioWatchdog::resume()
121{
122 AutoMutex _l(mMyLock);
123 if (mPaused) {
124 mPaused = false;
125 mMyCond.signal();
126 }
127}
128
129void AudioWatchdog::setDump(AudioWatchdogDump *dump)
130{
131 mDump = dump != NULL ? dump : &mDummyDump;
132}
133
134} // namespace android