android audioserver裏面用於binder調用超時檢測有個TimeCheck機制,對於audioserver binder調用不能超過5s,如果超過5s就會產生一個abort的log
實現路徑在frameworks\av\media\libmedia\TimeCheck.cpp
代碼量很少,但設計很巧妙
TimeCheck頭文件定義如下
class TimeCheck {
public:
// The default timeout is chosen to be less than system server watchdog timeout
static constexpr uint32_t kDefaultTimeOutMs = 5000;
TimeCheck(const char *tag, uint32_t timeoutMs = kDefaultTimeOutMs);
~TimeCheck();
private:
class TimeCheckThread : public Thread {
public:
TimeCheckThread() {}
virtual ~TimeCheckThread() override;
nsecs_t startMonitoring(const char *tag, uint32_t timeoutMs);
void stopMonitoring(nsecs_t endTimeNs);
private:
// RefBase
virtual void onFirstRef() override { run("TimeCheckThread", PRIORITY_URGENT_AUDIO); }
// Thread
virtual bool threadLoop() override;
Condition mCond;
Mutex mMutex;
// using the end time in ns as key is OK given the risk is low that two entries
// are added in such a way that <add time> + <timeout> are the same for both.
KeyedVector< nsecs_t, const char*> mMonitorRequests;
};
static sp<TimeCheckThread> getTimeCheckThread();
const nsecs_t mEndTimeNs;
};
}; // namespace android
kDefaultTimeOutMs 爲默認的超時檢測參數,這裏設置的是5s
TimeCheckThread是TimeCheck的一個內部類,繼承Thread類
實現代碼也不多,如下
/* static */
sp<TimeCheck::TimeCheckThread> TimeCheck::getTimeCheckThread()
{
static sp<TimeCheck::TimeCheckThread> sTimeCheckThread = new TimeCheck::TimeCheckThread();
return sTimeCheckThread;
}
TimeCheck::TimeCheck(const char *tag, uint32_t timeoutMs)
: mEndTimeNs(getTimeCheckThread()->startMonitoring(tag, timeoutMs))
{
}
TimeCheck::~TimeCheck() {
getTimeCheckThread()->stopMonitoring(mEndTimeNs);
}
TimeCheck::TimeCheckThread::~TimeCheckThread()
{
AutoMutex _l(mMutex);
requestExit();
mMonitorRequests.clear();
mCond.signal();
}
nsecs_t TimeCheck::TimeCheckThread::startMonitoring(const char *tag, uint32_t timeoutMs) {
Mutex::Autolock _l(mMutex);
nsecs_t endTimeNs = systemTime() + milliseconds(timeoutMs);
for (; mMonitorRequests.indexOfKey(endTimeNs) >= 0; ++endTimeNs);
mMonitorRequests.add(endTimeNs, tag);
mCond.signal();
return endTimeNs;
}
void TimeCheck::TimeCheckThread::stopMonitoring(nsecs_t endTimeNs) {
Mutex::Autolock _l(mMutex);
mMonitorRequests.removeItem(endTimeNs);
mCond.signal();
}
bool TimeCheck::TimeCheckThread::threadLoop()
{
status_t status = TIMED_OUT;
const char *tag;
{
AutoMutex _l(mMutex);
if (exitPending()) {
return false;
}
nsecs_t endTimeNs = INT64_MAX;
// KeyedVector mMonitorRequests is ordered so take first entry as next timeout
if (mMonitorRequests.size() != 0) {
endTimeNs = mMonitorRequests.keyAt(0);
tag = mMonitorRequests.valueAt(0);
}
const nsecs_t waitTimeNs = endTimeNs - systemTime();
if (waitTimeNs > 0) {
status = mCond.waitRelative(mMutex, waitTimeNs);
}
}
LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "TimeCheck timeout for %s", tag);
return true;
}
使用起來也很簡單,充分利用了C++的構造和析構函數
在 BnAudioPolicyService::onTransact即audiosever開始通過binder調用的地方,創建了一個TimeCheck對象
status_t BnAudioPolicyService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
..................
..................
TimeCheck check("IAudioPolicyService");
switch (code) {
case SET_DEVICE_CONNECTION_STATE: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
audio_devices_t device =
static_cast <audio_devices_t>(data.readInt32());
audio_policy_dev_state_t state =
static_cast <audio_policy_dev_state_t>(data.readInt32());
const char *device_address = data.readCString();
const char *device_name = data.readCString();
if (device_address == nullptr || device_name == nullptr) {
ALOGE("Bad Binder transaction: SET_DEVICE_CONNECTION_STATE for device %u", device);
reply->writeInt32(static_cast<int32_t> (BAD_VALUE));
} else {
reply->writeInt32(static_cast<uint32_t> (setDeviceConnectionState(device,
state,
device_address,
device_name)));
}
return NO_ERROR;
} break;
....................
.....................
}
TimeCheck在構造的時候調用了getTimeCheckThread()->startMonitoring(tag, timeoutMs);
getTimeCheckThread()裏new了TimeCheckThread對象,並使用強指針修飾,強指針在第一次引用的時候就會調用該類的onFirstRef方法:
virtual void onFirstRef() override { run("TimeCheckThread", PRIORITY_URGENT_AUDIO); }
那麼這個TimeCheckThread就會立即運行起來了
該線程調用mCond.waitRelative(mMutex, waitTimeNs);在這裏等待
如果binder調用很快就返回,再5s以內,那麼onTransact結束之後TimeCheck的析構會自動執行
TimeCheck::TimeCheckThread::~TimeCheckThread()
{
AutoMutex _l(mMutex);
requestExit();
mMonitorRequests.clear();
mCond.signal();
}
requestExit通知TimeCheckThread退出,同時也在超時範圍內喚醒等待mCond條件變量的TimeCheckThread,這樣線程正常退出,整個調用流程也正常通過
如果在設定的超時時間調用沒有返回,即TimeCheck沒有及時析構,那麼到了waitRelative就回返回一個TIMED_OUT的狀態,
LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "TimeCheck timeout for %s", tag);
這句將會執行併產生一個assert的log,通過堆棧打印出來