AudioServer的TimeCheck机制

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值