fix(coordinator): clear focus_target on cancel regardless of phase
finish_cancel_session_state previously only cleared state.focus_target when the cancelled phase was not Processing — Processing-phase cancel left the stale window-resource handle in place until the next begin_session_state overwrote it. Between cancel and next begin, any reader of focus_target sees a value belonging to a session that is about to be torn down. Move state.focus_target = None ahead of the phase check so it always fires when the cancel was accepted (i.e. not rejected for Idle / Inserting phases). Phase transition stays conditional on the original non-Processing branch, since end_session must still own the Processing → Idle collapse to avoid racing with polish/insert. Tighten cancel_session_state_machine_is_table_driven so it asserts focus_target gets cleared for every accepted phase (including Processing) and stays untouched on rejected cancels. Audit ID 3.3.5.
This commit is contained in:
1 parent
b1c387265b
commit
ce82fcd9fa
1 file changed
+18
-4
@@ -164,11 +164,14 @@ pub(crate) fn begin_cancel_session_state(state: &mut SessionState) -> Option<Can
|
||||
})
|
||||
}
|
||||
|
||||
/// cancel_session 外部资源清理后的锁内收尾。Processing 交给 end_session 自己收尾。
|
||||
/// cancel_session 外部资源清理后的锁内收尾。Processing 阶段把 phase 留给 end_session
|
||||
/// 自己收尾(防止与 polish/insert 路径竞争),但 focus_target 是当前 session 的窗口
|
||||
/// 资源句柄,cancel 之后无论处于哪个 phase 都应当释放,避免下一个 session 之前的
|
||||
/// 空档期被旧值污染。详见 audit 3.3.5。
|
||||
pub(crate) fn finish_cancel_session_state(state: &mut SessionState, decision: CancelDecision) {
|
||||
state.focus_target = None;
|
||||
if decision.phase != SessionPhase::Processing {
|
||||
state.phase = SessionPhase::Idle;
|
||||
state.focus_target = None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,8 +370,19 @@ mod tests {
|
||||
|
||||
assert_eq!(state.phase, expected_phase, "initial={initial:?}");
|
||||
assert_eq!(state.cancelled, expected_cancelled, "initial={initial:?}");
|
||||
if matches!(initial, SessionPhase::Starting | SessionPhase::Listening) {
|
||||
assert!(state.focus_target.is_none(), "initial={initial:?}");
|
||||
// 任何被 begin_cancel_session_state 接受的 phase(即非 Idle/Inserting)
|
||||
// 都应当清掉 focus_target,包括 Processing —— 这是 audit 3.3.5 的回归卡。
|
||||
if expected_cancelled {
|
||||
assert!(
|
||||
state.focus_target.is_none(),
|
||||
"focus_target should clear after cancel, initial={initial:?}"
|
||||
);
|
||||
} else {
|
||||
assert_eq!(
|
||||
state.focus_target,
|
||||
Some(1),
|
||||
"rejected cancel must not touch focus_target, initial={initial:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user