关于gst_element_set_state死锁问题调查
■现象
gst_element_set_state 变更GST_STATE_NULL状态时deadlock。
■再现步骤
1.Carlife语音识别时说”鲜花”
2.在VR语音提示音的时候播放过程中,按radio硬按键切源
更普遍的再现方法:
(也就是说该问题是普遍性问题,与Carlife无直接关系)
1.插入U盘播放音乐
2.音乐播放过程中,点击暂停
3.使用gst-launch-1.0 playbin uri=file:///media/disk/Track02.mp3 播放音乐
4.步骤3音乐播放过程中按radio硬按键切源
5.按Ctrl+C 结束步骤3的音乐播放,发现无法结束,终端输出以下内容后hangup。
Setting pipeline to PAUSED …
■直接原因
调用 gst_element_set_state() 函数暂停播放时会调用到alsasink的 gst_alsasink_reset() 函数来重置PCM流,该函数会获取alsa_lock锁,以保证当前正在处理的Buffer中的数据处理结束后重置PCM流。
由于alsasink的 gst_alsasink_write() 函数获取alsa_lock锁之后,无法写入最后一包数据,导致该锁无法释放,最终导致 gst_element_set_state() 死锁。
也就是说问题发生的时候,声卡的文件句柄无法正常写入。
err = snd_pcm_writei() 的返回值是:-11。
snd_strerror(err) 打印出来的含义是:”Resource temporarily unavailable”。
■根本原因
切源的时候DSP那边通路被切走,导致alsa的最后一包数据写不进去。
此时可以关声卡,但是不能写入数据。通路再切回来就能正常写入,声卡就可以关闭结束。
■调查详细
1)由于Carlife服务线程较多,为了准确定位是哪个线程,设置了线程名”SetNameForA7”
1 | (gdb) info threads |
★处是我们要找的”SetNameForA7”线程
2)正常情况下线程2718的状态
1 | (gdb) thread 18 |
该线程用于接收来自手机Carlife的命令通道的消息,recv是阻塞式函数,无消息时阻塞在recv处,属于正常情况。
3)出问题的时候,线程2718的状态
1 | (gdb) thread 17 |
可以看出是libgstalsa.so中获取互斥锁的地方deadlock了,此时由于gstreamer的调试信息未打开,所以无法看到完整的调用关系。
4)重新编译gstreamer1.0-plugins-base插件,加-g参数增加调试信息,重新打印堆栈回溯
1 | (gdb) bt |
此时可以看出完整的Gstreamer设置管道状态的调用顺序。
5)通过阅读alsasink插件的代码,发现只有gst_alsasink_write(),gst_alsasink_reset()两个函数会获取alsa_lock锁。
通过代码分析,唯一可能的原因是 gst_alsasink_write() 中调用 snd_pcm_writei() 函数一直失败。
6)使用” export GST_DEBUG=3,alsa:7”打开alsa插件的日志,下面的日志证实了步骤5)的猜想。
■附录:
下面是 gst_alsasink_write() 和 gst_alsasink_reset() 函数的代码。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34static void
gst_alsasink_reset (GstAudioSink * asink)
{
GstAlsaSink *alsa;
gint err;
alsa = GST_ALSA_SINK (asink);
GST_ALSA_SINK_LOCK (asink);
GST_DEBUG_OBJECT (alsa, "drop");
CHECK (snd_pcm_drop (alsa->handle), drop_error);
GST_DEBUG_OBJECT (alsa, "prepare");
CHECK (snd_pcm_prepare (alsa->handle), prepare_error);
GST_DEBUG_OBJECT (alsa, "reset done");
GST_ALSA_SINK_UNLOCK (asink);
return;
/* ERRORS */
drop_error:
{
GST_ERROR_OBJECT (alsa, "alsa-reset: pcm drop error: %s",
snd_strerror (err));
GST_ALSA_SINK_UNLOCK (asink);
return;
}
prepare_error:
{
GST_ERROR_OBJECT (alsa, "alsa-reset: pcm prepare error: %s",
snd_strerror (err));
GST_ALSA_SINK_UNLOCK (asink);
return;
}
}
1 | static gint |