Files
FlowScope/docs/reviews/p2-precheck-fifth-review.md
2026-05-21 17:00:16 +08:00

185 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# FlowScope P2 前第五轮复审意见
日期2026-05-21
审查对象:当前工作树中 `UIPreloadService``UIPreloadServiceTests` 的未提交修改
审查范围:`ReleaseAll()` 后新旧 in-flight preload 隔离、旧任务完成清理、相关 PlayMode 测试、generated csproj 编译状态。
审查结论:第四轮指出的 P1 gate 已收口;当前没有发现新的 P2 阻塞级问题。剩余主要是 `Release<TPanel>()``ReleaseAll()` 的 in-flight 语义不完全一致,可作为 P2 前小修或 P2 内 API polish。
---
## 本轮确认已收口
### 1. `ReleaseAll()` 后旧 in-flight 与新 preload 已隔离
相关文件:
- `My project/Assets/FlowScope/Runtime/UI/UIPreloadService.cs`
- `My project/Assets/FlowScope/Tests/PlayMode/UI/UIPreloadServiceTests.cs`
当前 `PreloadAsync` 在发现已有 in-flight 但 generation 过期时,会创建新的 `InFlightPreload`
```csharp
if (!_inFlightLoads.TryGetValue(panelType, out inFlightLoad) ||
inFlightLoad.ReleaseGeneration != _releaseGeneration)
{
var attribute = UIManager.GetPanelAttribute(panelType);
path = UIManager.ResolvePath(panelType, attribute);
inFlightLoad = new InFlightPreload();
inFlightLoad.ReleaseGeneration = _releaseGeneration;
_inFlightLoads[panelType] = inFlightLoad;
startLoad = true;
}
```
评价上一轮指出的“ReleaseAll 后、旧 load 完成前再次 preload 同一 panel 会复用旧任务并最终不缓存”的问题已经修正。
---
### 2. 旧 in-flight 完成时不会误删新 in-flight
当前 `LoadAndStoreAsync` 不再直接 `_inFlightLoads.Remove(panelType)`,而是只移除当前任务对应的 in-flight
```csharp
private void ForgetInFlightLoad(Type panelType, InFlightPreload inFlightLoad)
{
if (_inFlightLoads.TryGetValue(panelType, out var current) &&
ReferenceEquals(current, inFlightLoad))
{
_inFlightLoads.Remove(panelType);
}
}
```
评价:这个修正是关键的。它避免旧任务完成或失败时把 release 后新建的 in-flight 条目删掉。
---
### 3. 新增测试覆盖了上一轮缺口
新增测试:
- `PreloadAsync_AfterReleaseAllWhileOldLoadInFlight_StartsNewLoadAndCachesNewHandle`
- `OldInFlightCompletion_AfterReleaseAll_DoesNotRemoveNewInFlight`
覆盖的真实边界:
1. 旧 preload 正在加载。
2. 调用 `ReleaseAll()`
3. 立刻再次 preload 同一 panel。
4. 新 preload 必须触发新的资源加载。
5. 旧任务完成后只能释放旧 handle不能污染或删除新任务。
6. 新任务完成后应缓存新 handle。
评价:这两条测试正好覆盖第四轮指出的 P1 风险。
---
## 当前未发现的 P2 阻塞项
从本轮审查范围看,以下上一轮 gate 已经闭环:
- `ShutdownAsync` best-effort cleanup。
- `UIPreloadService` 首个 caller cancellation 不再污染其他 waiter。
- `ReleaseAll/Dispose` 后旧异步任务不再写回缓存。
- `ReleaseAll` 后重新 preload 同一 panel 不再复用旧 generation 任务。
- 旧任务完成不会误删新 in-flight。
因此,如果 P2 的第一项不是“正式发布资源后端/Addressables 集成质量”,当前 Game Core 主线可以进入 P2。
---
## 中风险改进
### `Release<TPanel>()` 对 in-flight preload 仍不生效
风险等级:中
建议P2 前可小修;若 P2 首项不扩 UI preload可放入 P2 内 polish。
证据文件:
- `My project/Assets/FlowScope/Runtime/UI/UIPreloadService.cs`
当前 `Release<TPanel>()` 只处理已经缓存到 `_handles` 的资源:
```csharp
if (!_handles.TryGetValue(panelType, out handle))
{
return;
}
_handles.Remove(panelType);
```
它不会处理同 panel 的 in-flight preload。结果是
1. `PreloadAsync<TPanel>()` 已启动但尚未完成。
2. 调用 `Release<TPanel>()`
3. 因为 `_handles` 里还没有缓存,`Release<TPanel>()` 直接 return。
4. 旧 preload 完成后仍会缓存 handle。
这与当前 `ReleaseAll()` 的语义不完全一致:`ReleaseAll()` 已经通过 `_releaseGeneration++` 明确让所有旧 in-flight 结果失效,而 `Release<TPanel>()` 仍只释放已缓存结果。
建议选一种语义并写清楚:
1. 如果 `Release<TPanel>()` 表示“释放该 panel 的预加载结果”,则应让该 panel 当前 in-flight 失效。
2. 如果 `Release<TPanel>()` 只表示“释放已缓存 handle不影响正在进行的 preload”则建议在 runtime usage guide 明确说明,避免使用者误以为它会取消或丢弃 in-flight 结果。
更一致的实现方向:
-`InFlightPreload` 增加 panel-level generation 或 invalidation flag。
- `Release<TPanel>()` 在释放 `_handles` 后,同时让当前 panel 的旧 in-flight 结果不能缓存。
- 补测试:`Release_WhenLoadInFlight_DoesNotCacheAndDisposesLoadedHandle`
---
## 低风险改进
### Addressables 真实 load/release 验证仍薄
当前 `AddressablesResourceBackendTests` 仍主要覆盖 `Name_ReturnsAddressables`
这不是本轮 UI preload 修复引入的问题,但仍是包分发或资源后端推广前的验收缺口。
建议:
- 如果 P2 首项是 package/editor tooling可放到 P2 内第一批验收。
- 如果 P2 首项是资源后端能力发布,则建议先补最小 Addressables PlayMode 集成或人工验收记录。
---
## 本轮验证
执行:
```powershell
dotnet build "My project\FlowScope.Tests.EditMode.csproj" --no-restore
dotnet build "My project\FlowScope.Tests.PlayMode.csproj" --no-restore
```
结果:
- EditMode generated csproj build0 error。
- PlayMode generated csproj build0 error。
- 仍有 Unity generated csproj 常见引用冲突 warning。
说明:
- 本轮未执行 Unity Test Runner。
- 本轮未做 MainMenuP0 人工场景验收。
- 本轮审查对象包含未提交工作树修改,不是新 commit。
---
## P2 Gate 判断
当前判断:可以准备进入 P2但建议先做一次收尾确认。
P2 前建议最小动作:
1. 决定 `Release<TPanel>()` 是否需要与 `ReleaseAll()` 一样使同 panel in-flight 失效。
2. 如果需要,补一个很小的测试和实现。
3. 如果暂不处理,在 runtime usage guide 记录 `Release<TPanel>()` 当前只释放已缓存 handle。
4. 跑一次 Unity Test Runner 或记录无法执行原因。
如果你希望严格一点,我建议先修 `Release<TPanel>()` 的 in-flight 语义,再正式开 P2如果按当前 P2 迭代节奏推进,它已经不是阻塞级问题。