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,通过堆栈打印出来