libstagefright looper unregisterStaleHandlers

今天看6.0的Alooper 实现 发现和4.1的实现不一样,边缘化了AlooperRoster 类,4.1中ALooperRoster 作为消息传递的中心负责调度消息。M版本中ALooperRoster 只是负责looper 对象keep alive 目前重要的操作都在looper message 中完成。

    详解一下unregisterStaleHandlers 函数:

ALooperRoster.cpp

void ALooperRoster::unregisterHandler(ALooper::handler_id handlerID) {
57    Mutex::Autolock autoLock(mLock);
58
59    ssize_t index = mHandlers.indexOfKey(handlerID);
60
61    if (index < 0) {
62        return;
63    }
64
65    const HandlerInfo &info = mHandlers.valueAt(index);
66
67    sp<AHandler> handler = info.mHandler.promote();
68
69    if (handler != NULL) {
70        handler->setID(0, NULL);
71    }
72
73    mHandlers.removeItemsAt(index);
74}
75
76void ALooperRoster::unregisterStaleHandlers() {
77
78    Vector<sp<ALooper> > activeLoopers;
79    {
80        Mutex::Autolock autoLock(mLock);
81
82        for (size_t i = mHandlers.size(); i > 0;) {
83            i--;
84            const HandlerInfo &info = mHandlers.valueAt(i);
85
86            sp<ALooper> looper = info.mLooper.promote();
87            if (looper == NULL) {
88                ALOGV("Unregistering stale handler %d", mHandlers.keyAt(i));
89                mHandlers.removeItemsAt(i);
90            } else {
91                // At this point 'looper' might be the only sp<> keeping
92                // the object alive. To prevent it from going out of scope
93                // and having ~ALooper call this method again recursively
94                // and then deadlocking because of the Autolock above, add
95                // it to a Vector which will go out of scope after the lock
96                // has been released.
97                activeLoopers.add(looper);
98            }
99        }
100    }
101}

ALooper.cpp

72ALooper::ALooper()
73    : mRunningLocally(false) {
74    // clean up stale AHandlers. Doing it here instead of in the destructor avoids
75    // the side effect of objects being deleted from the unregister function recursively.
76    gLooperRoster.unregisterStaleHandlers();
77}
78
79ALooper::~ALooper() {
80    stop();
81    // stale AHandlers are now cleaned up in the constructor of the next ALooper to come along
82}

这个函数调用出现的原因是为了推迟Mutex::Autolock autoLock(mLock) 抢占,避免AutoLock 在调用栈中

递归加锁,发生死锁。

问题场景:
       

41AAA::AAA(pid_t pid)
42    :
49      mLooper(new ALooper),
 {
55    ALOGV("AAA(%p)", this);
56    mLooper->setName("AAA Looper");
58    mLooper->start(
59            false, /* runOnCallingThread */
60            true,  /* canCallJava */
61            PRIORITY_AUDIO);
62
63    mHandler = new Handler(pid);
64    mLooper->registerHandler(mHandler);
73}
74
75AAA::~AAA() {
76    ALOGV("~AAA(%p)", this);
77    mLooper->stop();
79    mLooper->unregisterHandler(mHandler->id());
81}

如果 ALooper 的构造函数和析构函数是下面的实现方式:

72ALooper::ALooper()
73    : mRunningLocally(false) {
77}
78
79ALooper::~ALooper() {
80    stop();
81    gLooperRoster.unregisterHandler(handlerID);
82}

    

        sp<AAA> testAAA = new testAAA();

        testAAA.clear();     

        这段代码的执行过程分解应该是这样的:

       sp<AAA> testAAA = new testAAA(); //调用testAAA构造函数--->Alooper 构造函数---->绑定Handler

       testAAA.clear(); // 调用Alooper 析构函数--->调用stop()---->调用unregisterHandler(handlerId)--->

                       调用autoLock(mLock) 获取锁---->  调用 handler->setID(0, NULL) reset AHandler 和 Alooper

之间的联系---> 这时候ALooper 的引用计数为0触发Alooper 析构函数--->又一次调用到gLooperRoster.unregisterHandler(handlerID)--->再一次调用autoLock(mLock) 获取锁失败被线程挂起--->死锁。

产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

我们需要打破其中一个条件;下面是实现是打破循环等待.

72ALooper::ALooper()
73    : mRunningLocally(false) {
74    // clean up stale AHandlers. Doing it here instead of in the destructor avoids
75    // the side effect of objects being deleted from the unregister function recursively.
76    gLooperRoster.unregisterStaleHandlers();
77}
78
79ALooper::~ALooper() {
80    stop();
81    // stale AHandlers are now cleaned up in the constructor of the next ALooper to come along
82}

在连续的栈帧调用中n层获得mlock 在栈帧n 释放之前n+1......都不能竞争同意吧锁。

目前的情况 testAAA中new Handler 正常思维是析构的时候要把Handler unregist ,unregist 会把与Handler相关的Alooper 关连断开,正常情况是这样的。但是异常情况是当handler 对Alooper 的引用断开之后,函数调用的方向转到ALooper 的析构,Alooper 也想释放与之相关的handler 会调用Handler unregist 。完了产生循环诉求了。

解决方案简单粗暴,Alooper 释放相关Handler 关系的操作,滞后到next Alooper 构造的时候。

          

            

猜你喜欢

转载自my.oschina.net/u/269082/blog/761917