280 歌单链路三修:开A→开B 竞态防串(5194 串数据) + 92/200 批边界 Load Failed + 歌单列表盘缓存 + 孤儿 .part 回收

ncm_app(REPORT §26, ncm 158ec367 未部署留主会话):
- 打开时快照 plid+代次:appstate_playlist_begin/open_gen/open_idx/reset +
  appstate_load_playlist_songs_ctx(快照 pl 不读 g_st.playlists[],detail 结果落
  staging 锁内原子发布,代次/目标不匹配判 NCM_ERR_STALE 不写全局);playlist_tick
  过期丢弃 + appstate_playlist_reset 清窗口防串。修 5194「我喜欢的音乐」3 首/串数据。
- appstate_song_ensure_range fill-and-continue:bn<m 部分返回/单批网络失败非致命
  (记 NCM_ERR_NOTFOUND/r2),已物化保留+占位行;worker 首屏 track_ids_count>0
  降级非致命(snack)。修 92/200 首歌单点开整页 Load Failed。
- 歌单列表盘缓存 <data_dir>/playlists.bin(magic+ver+ts+数组):home_reload 先缓存
  秒渲 + 后台静默刷新,刷新失败有数据不置错态。修返回首页卡 LOADING(275 遗留)。
- 孤儿 .part/.tmp:evict/total_bytes LRU 扫描顺续删 mtime>7 天(in-flight mtime 新鲜安全)。
验证:新 bench/playlist_race_sim(竞态防串/批边界/非致命/盘缓存/reset)+cache_evict_sim
case9(7 天回收)+window/pagination/search/stream_sim 零回归+test_ncm_core host+mipsel
PASS;build_ncm.sh -Wall -Wextra 0 warning(text 978354/bss 1496484);0 真实请求。
遗留:appstate_logout 未联动删 playlists.bin;g_st.playlists[] 快照拷贝仍有极小交错窗。
This commit is contained in:
MeowOS committed 2026-09-01 21:06:57 +08:00
1 parent d8eeef5c57
commit f0b73e119d
11 files changed
+744 -48

No files matched your search

+27 -2
View File
@@ -20,6 +20,10 @@
#define MAX_LIMIT_GB 100
#define MAX_ENTRIES 4096
/* §26 孤儿半成品回收:.tmp/.part 不进 LRU 池(防在播文件误删),但长期不续传的残体占磁盘。
* LRU 扫描时顺手判 mtime——超过此天数的孤儿 .part/.tmp 惰性删除(in-flight 下载 mtime 新鲜安全)。 */
#define NCM_ORPHAN_PART_DAYS 7
typedef struct entry {
char name[256];
uint64_t size;
@@ -169,10 +173,21 @@ int ncm_cache_evict(const ncm_cache_t *c) {
if (!ents) { closedir(d); return NCM_ERR_OOM; }
int n = 0;
uint64_t total = 0;
int64_t orphan_cutoff = (int64_t)time(NULL) - (int64_t)NCM_ORPHAN_PART_DAYS * 86400;
struct dirent *e;
while ((e = readdir(d)) && n < MAX_ENTRIES) {
if (strncmp(e->d_name, "song_", 5) != 0) continue;
if (is_partial_name(e->d_name)) continue; /* 阶段3:.tmp/.part 归写者管理域,不淘汰 */
if (is_partial_name(e->d_name)) { /* 阶段3:.tmp/.part 归写者管理域,不进 LRU 池 */
/* §26 顺手续删:超过 7 天的孤儿 .part/.tmp(用户不再点该曲、不续传)惰性删除。
* 在播/刚中断的 .tmp/.part mtime 新鲜(写端持续更新),不会被误删。 */
char pb[NCM_CACHE_DIR_MAX + NAME_MAX + 8];
snprintf(pb, sizeof pb, "%s/%s", c->dir, e->d_name);
struct stat sb;
if (stat(pb, &sb) == 0 && S_ISREG(sb.st_mode) &&
(int64_t)sb.st_mtim.tv_sec < orphan_cutoff)
unlink(pb);
continue;
}
char p[NCM_CACHE_DIR_MAX + NAME_MAX + 8];
snprintf(p, sizeof p, "%s/%s", c->dir, e->d_name);
struct stat st;
@@ -307,10 +322,20 @@ uint64_t ncm_cache_total_bytes(const ncm_cache_t *c) {
DIR *d = opendir(c->dir);
if (!d) return 0;
uint64_t total = 0;
int64_t orphan_cutoff = (int64_t)time(NULL) - (int64_t)NCM_ORPHAN_PART_DAYS * 86400;
struct dirent *e;
while ((e = readdir(d))) {
if (strncmp(e->d_name, "song_", 5) != 0) continue;
if (is_partial_name(e->d_name)) continue; /* 阶段3:半成品不计入池占用 */
if (is_partial_name(e->d_name)) { /* 阶段3:半成品不计入池占用 */
/* §26 顺手续删:超过 7 天的孤儿 .part/.tmp 惰性删除。 */
char pb[NCM_CACHE_DIR_MAX + NAME_MAX + 8];
snprintf(pb, sizeof pb, "%s/%s", c->dir, e->d_name);
struct stat sb;
if (stat(pb, &sb) == 0 && S_ISREG(sb.st_mode) &&
(int64_t)sb.st_mtim.tv_sec < orphan_cutoff)
unlink(pb);
continue;
}
char p[NCM_CACHE_DIR_MAX + NAME_MAX + 8];
snprintf(p, sizeof p, "%s/%s", c->dir, e->d_name);
struct stat st;
+1
View File
@@ -31,6 +31,7 @@ enum {
NCM_ERR_AUTH = -13, /* 未登录/鉴权失败 */
NCM_ERR_LIMIT = -14, /* 超出上限级 */
NCM_ERR_VIP = -15, /* VIP 专享(fee 门槛命中,未发 song_url 请求,§20) */
NCM_ERR_STALE = -16, /* 异步结果过期(打开歌单 A 未完→打开 B,A 的迟到结果被丢弃,§26) */
};
/* ---------- 常用尺寸 ---------- */