一个camera死锁问题的分析与解决

       最近处理一个bug,手机自带相机app和三方app全部无法打开相机。哪怕app退出再次进入也无法打开相机,只能重启手机了。

        打开log,发现如下出错信息:        

        04-14 14:53:55.629 1427 3487 E CameraService: CameraService::connect (PID -1) rejected (too many other clients connecting).

        结合下面的源码,可知是因为connect CameraService 的时候无法拿到mServiceLockWrapper 锁而导致CameraService一直无法连接成功。


        接下来继续查找使用mServiceLockWrapper 锁的函数,其中有个函数“evictClientIdByRemote” 也在使用这个锁。这个函数的作用是在app异常死掉后,CameraService做清理的,包括移除激活的client和关闭client打开的相机。那么会不会是这个函数拿到锁后未释放呢?

        非常幸运的是,在相机无法使用的情况下,测试人员尝试在微信中打开相机,微信出现了一次ANR, ANR log 中会有每个进程以及线程的堆栈信息。这好比是在一个案发现场,正好装有视频监控,这对破案作用太关键了。赶紧打开ANR文件搜索“evictClientIdByRemote”,果然有。

        可以确定的是“evictClientIdByRemote”函数拿到mServiceLockWrapper 锁后,被挂住,导致mServiceLockWrapper 锁未能释放。从上图的堆栈可以知道,Camera2ClientBase类的“disconnect”函数正挂在MutexLock函数中。

        继续撸代码,“disconnect”函数函数中只使用一个锁“mBinderSerializationLock”,搜代码发现这个锁,只在“disconnect”和“connect”两个函数中使用,继续在ANR文件中搜索这两个函数,果然找到了。

        从图中堆栈结合源码可以知道。另外一个线程中的“disconnect”函数已经获得“mBinderSerializationLock”,在IPC调用flush的时候没有返回。flush真正的处理是在camera_provider 进程中。先看一下camera_provider 进程中flush的堆栈信息,“OverrideFlush”函数在wait是被挂住。


         结合“OverrideFlush”函数的源码,可以知道在Flush执行的时候,camera privider正处在recovery progress 中,所以要wait recovery progress完成后再执行下一步Flush操作。wait没有返回的原因是recovery未能完成。继续在ANR 堆栈中搜索“recovery”,看看“recovery”过程中是否有被挂住的地方。 非常幸运,一下就搜到了。


        从上图中的堆栈可以看到recovery progress 最终挂在了DeferredRequestQueue::Pause函数中。

        从DeferredRequestQueue::Pause函数的源码,我们可以看出,m_pDRQPauseLock->Lock()函数被挂住了。m_pDRQPauseLock 这个锁使用的地方不多,也全部在DeferredRequestQueue这个类中。我们继续在ANR文件中搜索“DeferredRequestQueue”,看看m_pDRQPauseLock这个锁被谁持有了。

                从上图的堆栈中,可以看出线程“2334”被挂在了DeferredRequestQueue::UpdateDependency的TimedWait函数中,再结合下图代码,可以知道UpdateDependency函数已经获得m_pDRQPauseLock 锁,但是却被m_pDRQPauseWait->TimedWait这条语句挂住了。

        这里的TimedWait是高通封装的pthread_cond_timedwait,Lock是封装的pthread_mutex_lock。这是经典的pthread_cond_wait与mutex结合的用法:

        Lock->if/while 判断条件->wait->UnLock.

         在调用pthread_cond_wait()前由本线程加锁(pthread_mutex_lock()),而在判断条件进入等待队列以前,mutex保持锁定状态,并在线程挂起进入等待后解锁。在条件满足或者超时后,离开pthread_cond_wait()/pthread_cond_timedwait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。

        TimedWait具有超时退出功能,查代码,超时时间是1秒。理论上即便没有signal发出,超时后会自动退出。现在却被长久挂住了,非常奇怪!

        突然发现,Lock使用的是m_pDRQPauseLock 锁,但是传给TimedWait的参数却使用了m_pDeferredQueueLock 锁。也就是TimedWait进入等待队列的时候释放了m_pDeferredQueueLock 锁,满足条件或者超时离开的时候是在对m_pDeferredQueueLock 加锁。所以被长久挂住的原因只能是m_pDeferredQueueLock已经被别的线程持有。继续在ANR堆栈中找线索。

        这个线程是id是“2338”,也是被挂在了DeferredRequestQueue::UpdateDependency函数中,只不过是挂住点不同,这个是挂在了Lock中。结合上面对线程“2334”的分析,"2334"线程获取了m_pDRQPauseLock 锁,在TimedWait时却释放了m_pDeferredQueueLock锁,超时后在获取m_pDeferredQueueLock锁被挂住。那么线程“2338”在线程“2334”进TimeWait释放m_pDeferredQueueLock锁后获取了m_pDeferredQueueLock锁,执行下一步获取m_pDRQPauseLock 锁被挂住。 这样死锁就形成了:

        线程“2334”持有m_pDRQPauseLock 锁,等待m_pDeferredQueueLock锁。

        线程“2338”持有m_pDeferredQueueLock锁,等待m_pDRQPauseLock 锁。

        分析完后,处理就非常简单了。这个问题应该是一个简单的typo,估计程序员在写代码的时候copy错了锁,修正就可以了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容