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,通過堆棧打印出來

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章