fix(audiod): 孤儿会话回收 — 客户端崩溃/被杀(sig=9)/正常退出后会话完整回收,STATE 回干净无流基线

根因: cli_free(EOF)与 STOP 路径只置 active=0 + 唤醒排空,未清 sid/written/played/underrun/
rms/peak/lvlseq——ncm 被内核 OOM 杀后面板 /api/overview audio 块残留(active=0 但 sid/统计挂着)。
修法: 两段式 session_end_locked(即时置死+清 sid/app_id,保留 rate/ch/fmt 供排空算 frame_size)+
session_clean_locked(播放线程排空完成后彻底归零 rate/ch/fmt/written/played/underrun/drop/stall/
rms/peak/lvlseq)。顺带修 dac_vol_set 一处 -Wmisleading-indentation。
宿主 51/51 PASS(47 既有 + T9 SIGKILL 孤儿回收断言 STATE 全清,模拟子进程崩溃). meow_audiod b122c9b8 交叉0warning
This commit is contained in:
AskaEth committed 2026-09-02 09:32:48 +08:00
1 parent 19f7e59927
commit 121d30ec95
5 files changed
+135 -27

No files matched your search

+38 -11
View File
@@ -111,8 +111,9 @@ static int g_logfd = -1;
static volatile sig_atomic_t g_stop = 0;
/* ---- 共享态(g_mu 保护 + g_cond 唤醒;g_cond 用 CLOCK_MONOTONIC,main 中初始化) ---- */
/* 前向声明(供 cli_free/dispatch 使用;定义见下) */
static void g_sid_reset(void);
/* 前向声明(供 cli_free/dispatch/播放线程使用;定义见下) */
static void session_end_locked(void);
static void session_clean_locked(void);
static void _audio_mark_reconfig_needed(void);
static pthread_mutex_t g_mu = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t g_cond;
@@ -556,7 +557,8 @@ static void dac_vol_set(int pct)
int t = 100 - pct, dac;
if (pct <= 0) dac = 255; /* 真静音(DS1039:255=-127.5dB) */
else dac = t * t * 192 / 10000; /* 感知平方锥度:dB 衰减=(1-pct)^2×96dB */
if (dac > 255) dac = 255; if (dac < 0) dac = 0; /* CS43198=衰减量(0最响255静音)+平方锥度(听感线性) */
if (dac > 255) dac = 255;
if (dac < 0) dac = 0; /* CS43198=衰减量(0最响255静音)+平方锥度(听感线性) */
amixer_set(1, dac, 1);
amixer_set(2, dac, 1);
logmsg("DAC vol -> %d%% (L/R=%d)", pct, dac);
@@ -1010,6 +1012,7 @@ static void *playback_thread(void *arg)
if ((draining && av < (size_t)fs) || paused) {
/* 排空完毕(或暂停):若设备仍开且已排空 → 关;否则等待 */
if (draining && g_dev_open) dev_close();
if (draining) session_clean_locked(); /* 流已死且排空完成:会话统计彻底归零 */
cond_wait_tmo(COND_WAIT_MS);
continue;
}
@@ -1065,18 +1068,44 @@ static void cli_free(struct client *c)
c->fd = -1;
if (g_st.active && g_st.sid == c->sid) {
logmsg("client EOF: stream %u recycled (app=%s)", (unsigned)c->sid, g_st.app_id);
g_st.active = 0;
g_sid_reset();
session_end_locked();
}
}
static void g_sid_reset(void)
/* ---- 流结束/会话回收(客户端死亡或显式 STOP 后,STATE 须回到干净无流基线) ----
* 两段式:
* - session_end_locked():流结束标记(停止/EOF 即时调用)。置 inactive + 清会话身份
* (sid/app_id)+ 唤醒播放线程排空 ring。**不清空 ring、不清 rate/ch/fmt**:未播完的缓冲
* 要写完(排空)才关设备,否则 tone/播放末尾被截断;排空期间 frame_size() 仍依赖 rate/ch/fmt。
* ring 的重置只发生在 OPEN 授予新流时(ring_rebuild)。
* - session_clean_locked():会话统计彻底清零(流已结束且 ring 排空完毕的迟后调用)——
* STATE 归零:rate/ch/fmt=0、written=played=0、underrun/drop/stall=0、rms/peak=-inf、lvlseq=0。
* 此刻 rate/ch/fmt 已不再被排空引用,可安全清零。txdrop 为全局客户端级(不随流清),不动。 */
static void session_end_locked(void)
{
/* 停止/EOF 只标记流结束 + 唤醒播放线程排空 ring(**不清空 ring**:未播完的缓冲要写完
* 才关设备,否则 tone/播放末尾被截断)。ring 的重置只发生在 OPEN 授予新流时(ring_rebuild)。 */
g_st.active = 0;
g_st.sid = 0;
g_st.app_id[0] = 0;
g_paused = 0;
g_reconfig_pending = 0;
pthread_cond_signal(&g_cond);
}
static void session_clean_locked(void)
{
g_st.rate = 0;
g_st.ch = 0;
g_st.fmt = 0;
g_written_frames = 0;
g_played_frames = 0;
g_underrun = 0;
g_drop_frames = 0;
g_stall = 0;
g_m_sumsq = 0;
g_m_peak = 0;
g_m_frames = 0;
g_m_win = 0;
g_lvl_rms_db10 = g_lvl_peak_db10 = METER_FLOOR_DB10;
g_lvl_seq = 0;
}
/* 处理一个客户端帧(调用方已持 g_mu) */
static void dispatch(struct client *c, uint16_t type, uint32_t req_id, const uint8_t *pl, uint32_t len)
@@ -1176,11 +1205,9 @@ static void dispatch(struct client *c, uint16_t type, uint32_t req_id, const uin
pthread_cond_signal(&g_cond);
else /* STOP */ {
logmsg("STOP sid=%u (app=%s)", (unsigned)sid, g_st.app_id);
g_st.active = 0;
g_st.sid = 0;
c->sid = 0;
c->state = CLS_OPEN;
g_sid_reset();
session_end_locked();
}
} else if (type == MSG_AUDIO_STOP) {
/* 非当前/已 draining 流:仅清客户端绑定 */