From c15d6657a17d7cef91f800f40d11760e2e7340af Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 30 May 2012 14:52:57 -0700 Subject: Add audio watchdog thread Change-Id: I4ed62087bd6554179abb8258d2da606050e762c0 --- services/audioflinger/AudioWatchdog.cpp | 134 ++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 services/audioflinger/AudioWatchdog.cpp (limited to 'services/audioflinger/AudioWatchdog.cpp') diff --git a/services/audioflinger/AudioWatchdog.cpp b/services/audioflinger/AudioWatchdog.cpp new file mode 100644 index 0000000..8f328ee --- /dev/null +++ b/services/audioflinger/AudioWatchdog.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AudioWatchdog" +//#define LOG_NDEBUG 0 + +#include +#include "AudioWatchdog.h" + +namespace android { + +void AudioWatchdogDump::dump(int fd) +{ + char buf[32]; + if (mMostRecent != 0) { + // includes NUL terminator + ctime_r(&mMostRecent, buf); + } else { + strcpy(buf, "N/A\n"); + } + fdprintf(fd, "Watchdog: underruns=%u, logs=%u, most recent underrun log at %s", + mUnderruns, mLogs, buf); +} + +bool AudioWatchdog::threadLoop() +{ + { + AutoMutex _l(mMyLock); + if (mPaused) { + mMyCond.wait(mMyLock); + // ignore previous timestamp after resume() + mOldTsValid = false; + // force an immediate log on first underrun after resume() + mLogTs.tv_sec = MIN_TIME_BETWEEN_LOGS_SEC; + mLogTs.tv_nsec = 0; + // caller will check for exitPending() + return true; + } + } + struct timespec newTs; + int rc = clock_gettime(CLOCK_MONOTONIC, &newTs); + if (rc != 0) { + pause(); + return false; + } + if (!mOldTsValid) { + mOldTs = newTs; + mOldTsValid = true; + return true; + } + time_t sec = newTs.tv_sec - mOldTs.tv_sec; + long nsec = newTs.tv_nsec - mOldTs.tv_nsec; + if (nsec < 0) { + --sec; + nsec += 1000000000; + } + mOldTs = newTs; + // cycleNs is same as sec*1e9 + nsec, but limited to about 4 seconds + uint32_t cycleNs = nsec; + if (sec > 0) { + if (sec < 4) { + cycleNs += sec * 1000000000; + } else { + cycleNs = 4000000000u; + } + } + mLogTs.tv_sec += sec; + if ((mLogTs.tv_nsec += nsec) >= 1000000000) { + mLogTs.tv_sec++; + mLogTs.tv_nsec -= 1000000000; + } + if (cycleNs > mMaxCycleNs) { + mDump->mUnderruns = ++mUnderruns; + if (mLogTs.tv_sec >= MIN_TIME_BETWEEN_LOGS_SEC) { + mDump->mLogs = ++mLogs; + mDump->mMostRecent = time(NULL); + ALOGW("Insufficient CPU for load: expected=%.1f actual=%.1f ms; underruns=%u logs=%u", + mPeriodNs * 1e-6, cycleNs * 1e-6, mUnderruns, mLogs); + mLogTs.tv_sec = 0; + mLogTs.tv_nsec = 0; + } + } + struct timespec req; + req.tv_sec = 0; + req.tv_nsec = mPeriodNs; + rc = nanosleep(&req, NULL); + if (!((rc == 0) || (rc == -1 && errno == EINTR))) { + pause(); + return false; + } + return true; +} + +void AudioWatchdog::requestExit() +{ + // must be in this order to avoid a race condition + Thread::requestExit(); + resume(); +} + +void AudioWatchdog::pause() +{ + AutoMutex _l(mMyLock); + mPaused = true; +} + +void AudioWatchdog::resume() +{ + AutoMutex _l(mMyLock); + if (mPaused) { + mPaused = false; + mMyCond.signal(); + } +} + +void AudioWatchdog::setDump(AudioWatchdogDump *dump) +{ + mDump = dump != NULL ? dump : &mDummyDump; +} + +} // namespace android -- cgit v1.1