fix(audiod): 长流卡死根治 — 防卡死护栏全量落地(AUDIOD_DESIGN §12)
根因(级联死锁): 内核驱动 wedge(hbc3000/cs43198 时钟域族, write/PREPARE/amixer ioctl 卡死在内核) → 播放线程永不返回 → ring 填满 → IO 线程在 ring_write 无超时 cond_wait 永久睡眠 → accept 停摆(backlog 满后 CLI ECONNREFUSED) → ncm 堵 socket 写 → 全链冻结。 同效路径: popen_read_tmo 无界 waitpid 随挂死的 amixer 子进程拖死持锁线程。 修法: - ring 写有界等待(MEOW_AUDIO_RING_WAIT_MS 缺省 5s)超时丢弃+计数(dropped=), IO 永生; 等待期 sid 代际守卫(抢占中等待的旧流 PCM 不再注入新流 ring) - 全部 cond 等待 timedwait 化(播放 1s 周期自愈, g_cond 改 CLOCK_MONOTONIC); 信号处理器只置 g_stop(不再调非 async-signal-safe 的 cond_signal) - popen 收割有界: WNOHANG 300ms → SIGKILL 300ms → D 态放弃收割(泄漏换永生); 路由线程 amixer 调用不持 g_mu; QUERY 改报 g_route_cur 缓存(IO 热路径不再 popen) - ALSA 改 O_NONBLOCK + poll(POLLOUT,1s): 连续 3 次停摆判 wedge → PREPARE 自救无效即 关卡进丢弃模式(stall=), 下一路 OPEN 重开重试 - server→client 下行帧单发 write 写不全即整帧丢弃(txdrop=), 杜绝半帧错位/慢客户端堵 IO - 主线程 watchdog: IO/播放心跳 10s 静默告警; io 线程 poll 出错不退出(listener 不丢); backlog 4→8; RESUME/PAUSE 补 cond 唤醒 - SDK meow_ui_audio.c + CLI: connect 非阻塞+poll 2s, SO_SNDTIMEO/RCVTIMEO 2s (audiod 真死时 App 报错重开, 不再永久阻塞) 验证: 宿主 38/38 PASS(旧 19 回归 + 新增 T5 长流 3min 等效 31.7MB 持续灌 played==written underrun/dropped=0 喂流中 listener 探活 15/15; T6 wedge 故障注入生存 — 旧代码同款注入 挂死(90s timeout 强制 kill), 新代码 IO/accept/preempt/SIGTERM 全活; T7 慢客户端 tx 丢弃); qemu-mipsel 静态二进制 status/tone 全通(written=played=13230 underrun=0); -Wall -Wextra 0 warning(host+mipsel+SDK 双端编译)。 meow_audiod md5 86a8b6793d516c90bbd12c2997b30d8f / meow_audio c23b96490842f54324b855fd9c2467a5 (fw_content.conf 已更)。暂不部署真机(主会话统一); meow_ui.a/App 重链留主会话。
This commit is contained in:
1 parent
243f328499
commit
e1e1331e44
10 files changed
+477
-67
No files matched your search
@@ -29,6 +29,7 @@
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
@@ -73,15 +74,39 @@ static const char *sock_path(void)
|
||||
return (e && e[0]) ? e : AUD_SOCK_DEFAULT;
|
||||
}
|
||||
|
||||
/* 连接 audiod。**全部有界**:connect 非阻塞 + poll 2s(对端 listen backlog 满/wedge 时
|
||||
* 阻塞 connect 会把 App 播放线程永久压进内核——真机长流卡死时 ncm 卡死的同款路径);
|
||||
* 建连后 SO_SNDTIMEO/RCVTIMEO 2s:audiod 停摆时 write 超时返回错误 → 标记流死,
|
||||
* App 走「重新 open」恢复路径,绝不永久阻塞。 */
|
||||
#define AUD_CONN_TMO_MS 2000
|
||||
static int connect_path(const char *path)
|
||||
{
|
||||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd < 0) return -1;
|
||||
int fl = fcntl(fd, F_GETFL, 0);
|
||||
if (fl >= 0) fcntl(fd, F_SETFL, fl | O_NONBLOCK);
|
||||
struct sockaddr_un a;
|
||||
memset(&a, 0, sizeof a);
|
||||
a.sun_family = AF_UNIX;
|
||||
strncpy(a.sun_path, path, sizeof a.sun_path - 1);
|
||||
if (connect(fd, (struct sockaddr *)&a, sizeof a) < 0) { close(fd); return -1; }
|
||||
int r = connect(fd, (struct sockaddr *)&a, sizeof a);
|
||||
if (r < 0 && errno == EINPROGRESS) {
|
||||
struct pollfd p;
|
||||
p.fd = fd; p.events = POLLOUT; p.revents = 0;
|
||||
if (poll(&p, 1, AUD_CONN_TMO_MS) > 0) {
|
||||
int err = 0;
|
||||
socklen_t el = sizeof err;
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &el) == 0 && err != 0) { errno = err; r = -1; }
|
||||
} else {
|
||||
r = -1; /* 超时/错误 */
|
||||
}
|
||||
}
|
||||
if (r < 0) { close(fd); return -1; }
|
||||
if (fl >= 0) fcntl(fd, F_SETFL, fl); /* 回阻塞模式;读写超时由 SO_*TIMEO 兜底 */
|
||||
struct timeval tv;
|
||||
tv.tv_sec = AUD_CONN_TMO_MS / 1000; tv.tv_usec = 0;
|
||||
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
|
||||
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user