fix(ncm §31): 修两处 UI 数据竞态——返回首页行空白(staging发布) + 进第二个歌单显示上一歌单(换歌单清窗)
A. 返回首页各行 2~3s 空白:appstate_load_playlists 就地 memset(g_st.playlists[]) 与 home_src2 无锁逐行绑定竞态,刷新期间 playlist_count 保留旧值 → UI 读清零行。 照搬 s_build[] staging 范式:网络落 s_pls[] → 成功后锁内原子发布(拷回+更新count), 失败保留旧快照(断网离线浏览)。绝不就地清零 UI 可见数组。 B. 进第二个歌单显示上一歌单:playlist_open/refresh 同步路径不调 appstate_playlist_reset, pl_apply(1) 绑到上一歌单残留窗口(track_ids_count/songs)。 新增窗口归属(歌单 id 唯一标识, 对搜索歌单同样成立): worker 首屏发布后 appstate_playlist_window_set;refresh 目标≠归属(窗口is) → 同步 reset 再 pl_apply。 同歌单手动刷新(id 同)保留旧内容(stale-while-revalidate)。 宿主回归:playlist_race_sim(新 F 并发读无清零态/G 开A→开B 不串)、window/search/ cache_evict/player_flow/stream_sim、test_ncm_core 宿主+mipsel 全 PASS; build_ncm.sh -Wall -Wextra 0 warning;0 真实请求;未部署。
This commit is contained in:
1 parent
97b741cf56
commit
10690bc348
9 files changed
+290
-9
No files matched your search
@@ -2633,3 +2633,94 @@ b731654a06d5ca5746edea8393f8668f ncm_host # 宿主调试二进
|
||||
- **`parse_url` http→443 潜性缺陷**:`http://` URL 无显式端口时 `url.port` 恒 443(TLS),对
|
||||
music.126.net CDN(443 上提供 TLS)恰好可用;语义错误但未阻断,未改(改动有回归风险)。
|
||||
- **未动 sdk/、vendor/**;`ncm_host`/`test_ncm_core_host` 为宿主调试产物随批重编。
|
||||
|
||||
## 31. UI 两处数据竞态修复:返回首页单行空白 + 新进歌单显示上一歌单(2026-09-02,REPORT §31)
|
||||
|
||||
> 真机「ncm 待修清单」⑤/⑥(见 WORK_STATE 2026-09-02 用户实测 8 条)。只改 `patch_v2/ncm_app/`。
|
||||
> 根因已由只读分析钉死,本批照方抓药实现。风控纪律:**0 次真实 NCM 请求**(全部离线 stub);
|
||||
> **未部署真机**(留主会话统一上卡)。
|
||||
|
||||
### 31.1 症状 A:返回首页各行 2~3 秒空白(除「我喜欢的音乐」外无标题/曲目数 0)
|
||||
|
||||
**根因**:`appstate_load_playlists`(`app/logic/appstate.c:182` 附近)对共享数组 `g_st.playlists[]`
|
||||
**就地 `memset`→网络回填**,与 UI 线程 `home_src2` **无锁逐行绑定**竞态。刷新期间 `playlist_count`
|
||||
保留旧值(cache),UI 读到被清零的行(`name[0]==0`/`track_count==0`)。下标 0 先读幸存(=「我喜欢的
|
||||
音乐」行正常的由来)。旧代码在**失败**时还把 `playlist_count=0`,覆盖 §26 cache 秒渲的「断网离线
|
||||
浏览」语义(§31.1 顺带修)。
|
||||
|
||||
**修法**(照搬本文件已有 `s_build[]` staging 范式):网络结果落 `static s_pls[NCM_PLAYLIST_MAX]`
|
||||
staging,**成功后锁内原子发布**(拷回 `g_st.playlists[]` + 更新 `playlist_count`),失败**保留旧快照**
|
||||
(不改 playlists/playlist_count)。绝不就地清零 UI 可见数组。新增 `g_pls_mu` 发布锁。
|
||||
|
||||
### 31.2 症状 B:进入第二个歌单显示上一个歌单内容
|
||||
|
||||
**根因**:`playlist_open/playlist_refresh` 的同步路径不调 `appstate_playlist_reset`——新页先
|
||||
`pl_apply(1)` 绑到上一歌单**残留窗口**(`track_ids_count`/`songs`),B 标题下画出 A 的行。
|
||||
|
||||
**修法**:`playlist_refresh` 起 worker **前**,若目标歌单与当前窗口归属不同则**同步调
|
||||
`appstate_playlist_reset()`** 再 `pl_apply`——换歌单必清;同歌单手动刷新(id 相同)保留旧内容
|
||||
(stale-while-revalidate)。窗口归属以**歌单 id** 唯一标识(对搜索歌单同样成立——不同搜索结果 =
|
||||
不同 id,避免搜索 A→搜索 B 仍被 idx 哨兵误判为同一歌单):
|
||||
|
||||
- `appstate.c` 新增 `appstate_playlist_window_set(plid)`/`appstate_playlist_window_is(plid)`(
|
||||
`g_winpl_mu` 保护);`appstate_playlist_reset` 顺带清空归属。
|
||||
- `playlist.c`:`playlist_worker` 首屏发布后(非 STALE)`appstate_playlist_window_set(tpl.id)`;
|
||||
`playlist_refresh` 在 `!appstate_playlist_window_is(g_tgt.pl.id)` 时同步 reset。
|
||||
|
||||
### 31.3 验证记录(0 真实 NCM 请求)
|
||||
|
||||
| 项 | 方式 | 结果 |
|
||||
|---|---|---|
|
||||
| A 并发读 vs 后台刷 | `bench/playlist_race_sim.c` **新 F**(reader 线程模拟 home_src2 逐行绑定 + writer 反复 `appstate_load_playlists`) | **PASS**:20000 次刷新全程 reader 读不到清零态(`name[0]==0`),最终数组完整 |
|
||||
| B 开 A→开 B | `bench/playlist_race_sim.c` **新 G**(load A 标记归属=A → 换 B 判归属→reset → 窗口清空 → load B) | **PASS**:切到 B 后 `track_ids_count==0 && song_count==0`(B 页不出现 A 行),B 首行 id 在 B 范围 |
|
||||
| 既有回归 | window_sim / search_sim(重建,编译 appstate.c) | 全 **PASS** |
|
||||
| 既有回归 | cache_evict_sim / stream_sim(PCM 金标准 16+resume)/ test_ncm_core 宿主+mipsel | 全 **PASS** |
|
||||
| 播放链回归 | player_flow_sim(重建 P1-P12) | **ALL PASS** |
|
||||
| 宿主离屏渲染 | `build_ncm_host.sh` + `NCM_PAGE=playlist/home`(mock) | exit=0,playlist 页 6 行正常(删 ` · ` tofu 后) |
|
||||
| mipsel 构建 | `bash build_ncm.sh` 自有代码 `-Wall -Wextra` | **0 warning**;`text 997202 / data 1648 / bss 1362468`(bss +~95KB = `s_pls[]` staging) |
|
||||
|
||||
### 31.4 md5(本批改动文件 + 产物)
|
||||
|
||||
```
|
||||
dd3d36d5935b85df8d8beabc52a0d090 ncm # mipsel 静态 App(新;未部署,留主会话)
|
||||
57c20e6e9a4b32631c9b6d5ac30463a6 ncm_host # 宿主离屏调试二进制(重链)
|
||||
7b1ebcc3688e49fc7b8264e4be7a46df playlist_race_sim # 重链(含新 F/G)
|
||||
5597307a1a95e06db40451085caae248 window_sim # 重链
|
||||
1eb36c7461f9ad47584481780132ea1c search_sim # 重链
|
||||
0cecfb3c38483549d83b32775a377c3b player_flow_sim # 重链(补 auto_av stub)
|
||||
c23f3152df9bf634e8cdd2a0f0b03697 app/logic/appstate.c # §31.1/31.2
|
||||
6d678c5f94a413e1daab4b01ef00febe app/ui/playlist.c # §31.2
|
||||
cb6fe604e433401302490dc43a3c7891 app/ncm_app_internal.h # 窗口归属 API 声明
|
||||
65afad5e3ad75b95cd544d4019f274dd bench/playlist_race_sim.c # 新 F/G + user_playlist 桩
|
||||
bed32f8ffccbbfb371556ee5e41cae37 bench/player_flow_sim.c # 新 ncm_api_song_url_auto_av 桩(测试桩)
|
||||
de4c5e1939c7571b90f75a80f48236c0 build_ncm_host.sh # SDK_SRC 补 meow_ui_str(宿主 I18N 链接)
|
||||
```
|
||||
|
||||
### 31.5 出入清单(只改 `patch_v2/ncm_app/`)
|
||||
|
||||
```
|
||||
修改:
|
||||
app/logic/appstate.c # §31.1 staging 发布(appstate_load_playlists)+ §31.2 窗口归属
|
||||
app/ui/playlist.c # §31.2 worker 标记归属 + refresh 换歌单清窗
|
||||
app/ncm_app_internal.h # appstate_playlist_window_set/is 声明
|
||||
bench/playlist_race_sim.c # 新 F/G 用例 + ncm_api_user_playlist/ncm_login_status_check/
|
||||
# ncm_ctx_clear_cookie 离线桩
|
||||
bench/player_flow_sim.c # 补 ncm_api_song_url_auto_av 桩(§20.3 新符号,测试桩,不改生产)
|
||||
build_ncm_host.sh # SDK_SRC 补 meow_ui_str(SDK I18N 拆分致宿主链接断,预存)
|
||||
REPORT.md # 本批记录(§31)
|
||||
(未动 sdk/、vendor/、core/、meow_launcherd/;未部署真机;0 真实请求)
|
||||
```
|
||||
|
||||
### 31.6 诚实出入 / 遗留
|
||||
|
||||
- **真机未部署未验**(留主会话统一上卡 `dd3d36d5`)。真机验收点:①返回首页各行不再空白(有标题/
|
||||
曲目数);②进第二个歌单不显示上一歌单内容;③断网回首页可离线浏览缓存列表(§31.1 顺带修)。
|
||||
- **staging 发布对 lock-free 读侧(home_src2/home_draw)非严格原子**:发布为 valid→valid 覆盖,
|
||||
`name[0]` 在旧/新快照恒非零,故「清零态」不可能被读到(与本批 F 断言一致);若想彻底消除
|
||||
理论上的一次字段级 torn 混合读,可另给 `g_st.playlists[]` 加读锁(本次未做,保持最小改动)。
|
||||
- **`player_flow_sim.c` 补的 `auto_av` 桩**:只为了在 §20.3(HEAD `97b741c`,主会话在做的降级链
|
||||
工作)把 `player_manager.c` 切到 `ncm_api_song_url_auto_av` 之后测试能构建;桩转发到旧 `auto`
|
||||
桩、不做档位链语义——`P1-P12` 全过说明该 sim 关注点(播放流/降级可访问性)未受我改动影响。
|
||||
§20.3 档位链语义本身的验证属主会话(297 队列项)。
|
||||
- **bss +~95KB**:`s_pls[NCM_PLAYLIST_MAX]`(120×808 ≈ 95KB)staging,与既存 `s_build[16384]`
|
||||
(128KB) 同口径;设备 RAM 56MB 可承受(总 bss 1.36MB)。
|
||||
Reference in new issue
Block a user