docs: clarify binding refresh and worker publication boundaries
This commit is contained in:
@@ -164,6 +164,7 @@ impl Worker {
|
||||
let Ok(mut current) = namespace.write() else {
|
||||
return error_response(StatusCode(500), "namespace lock poisoned");
|
||||
};
|
||||
// 锁外退役期间可能接受更新提交;过期请求不能再删除当前世代文档中的同编号窗口。
|
||||
if current.submission != epoch {
|
||||
return superseded(epoch);
|
||||
}
|
||||
@@ -186,12 +187,14 @@ impl Worker {
|
||||
document.frame.session = 0;
|
||||
document.frame.focus = None;
|
||||
for view in document.windows.values_mut() {
|
||||
// 丢弃的是整个共享 VM,所以剩余每个副窗都撤销交互身份,不能只清主画面或刚关闭的那个窗口。
|
||||
view.frame.session = 0;
|
||||
view.frame.focus = None;
|
||||
}
|
||||
current.runtime_session = 0;
|
||||
current.window_commands.clear();
|
||||
}
|
||||
// 发布退役后的完整文档;只有 Some 才继续移除视口记录并唤醒 GUI,过期或校验失败都提前返回。
|
||||
match current.commit(epoch, document) {
|
||||
Ok(Some(_)) => {}
|
||||
Ok(None) => return superseded(epoch),
|
||||
@@ -283,6 +286,7 @@ impl Worker {
|
||||
// success 可仅有命令而无新帧;错误发布故障条,panic 还要丢弃 VM 与命令世代。
|
||||
let (document, commands) = match result {
|
||||
Ok(Ok((document, commands))) => {
|
||||
// 背景轮询成功有两类结果:有新文档则走下面原子提交,无文档则仅发布可能存在的窗口命令。
|
||||
if let Some(document) = document {
|
||||
(document, Some(commands))
|
||||
} else {
|
||||
@@ -290,6 +294,7 @@ impl Worker {
|
||||
let projected = !commands.is_empty();
|
||||
current.publish_window_commands(id, commands);
|
||||
drop(current);
|
||||
// 已释放 Namespace 写锁后才唤醒 GUI;没有命令也没有新帧时保持空闲,不凭轮询次数触发绘制。
|
||||
if projected {
|
||||
notify();
|
||||
}
|
||||
@@ -320,10 +325,12 @@ impl Worker {
|
||||
};
|
||||
// 只有提交成功才发布同世代原生命令和通知 GUI,错误/过期结果不能算一次有效刷新。
|
||||
let changed = matches!(current.commit(epoch, document), Ok(Some(_)));
|
||||
// 有效文档和对应命令属于同一会话,提交未接受时不能单独交付命令造成画面/原生窗口状态错配。
|
||||
if changed && let Some(commands) = commands {
|
||||
current.publish_window_commands(id, commands);
|
||||
}
|
||||
drop(current);
|
||||
// changed 来自权威 commit 结果,不把失败或被替代的候选当作可见更新通知。
|
||||
if changed {
|
||||
notify();
|
||||
}
|
||||
@@ -699,6 +706,7 @@ impl Worker {
|
||||
// 是否需对可见帧加故障条取决于 VM 的实际执行状态,普通编译失败不等于整个域已损坏。
|
||||
let faulted = session.is_faulted();
|
||||
if session.has_pending_refresh() {
|
||||
// 只有正常完成业务输入的结果可保存在 PendingInput;恢复时沿用 result,不再执行一次原始输入。
|
||||
if let Ok(Ok((result, _))) = result {
|
||||
return RequestProgress::Pending(PendingRequest::Input(Box::new(PendingInput {
|
||||
epoch,
|
||||
@@ -777,6 +785,7 @@ impl Worker {
|
||||
"input runtime was discarded",
|
||||
));
|
||||
};
|
||||
// 接受序号与真实 VM 会话号都须匹配;同 epoch 中若换过会话,也不能把旧 PendingInput 接到新堆续跑。
|
||||
if *epoch != input.epoch || session.id != input.id {
|
||||
return RequestProgress::Complete(superseded(job.submission));
|
||||
}
|
||||
@@ -858,6 +867,7 @@ impl Worker {
|
||||
// rendered 只表示此次交付带新文档,成功执行但页面未变仍可返回 value/session;不因此虚增 revision。
|
||||
let rendered = result.document.is_some();
|
||||
if let Some(document) = result.document {
|
||||
// 可见文档必须先完整验证并被当前 epoch 接受,再发布其窗口命令;不能先回成功响应后补提交。
|
||||
match current.commit(epoch, document) {
|
||||
Ok(Some(_)) => {}
|
||||
Ok(None) => return superseded(job.submission),
|
||||
@@ -868,6 +878,7 @@ impl Worker {
|
||||
// 在释放锁前读取这次确定的 revision,响应不再为字符串化借用共享状态。
|
||||
let revision = current.revision;
|
||||
drop(current);
|
||||
// 有新画面或仅有原生命令都要通知;纯值计算没有两者时不强行重绘,响应仍正常返回值。
|
||||
if rendered || projected {
|
||||
notify();
|
||||
}
|
||||
@@ -887,6 +898,7 @@ impl Worker {
|
||||
.unwrap_or_else(|| format!("{error:#}").chars().take(1024).collect());
|
||||
document.frame.fault = Some(fault.clone());
|
||||
for view in document.windows.values_mut() {
|
||||
// 这些副窗共享已故障的持久 VM,同步显示同一原因,不能只在发生事件的一个窗口标记故障。
|
||||
view.frame.fault = Some(fault.clone());
|
||||
}
|
||||
// 尽力提交故障条,主响应仍保留原始执行错误;不能因二次故障覆盖真正原因。
|
||||
@@ -902,6 +914,7 @@ impl Worker {
|
||||
document.frame.fault =
|
||||
Some("Runtime discarded after an internal execution failure".into());
|
||||
for view in document.windows.values_mut() {
|
||||
// panic 后保留最后完整画面并附统一故障条,下面会撤销运行时资格,不能继续向旧对象派发输入。
|
||||
view.frame.fault = document.frame.fault.clone();
|
||||
}
|
||||
// 输入 panic 保留最近画面但撤销运行时/命令并发布故障条,再将 self.repl 丢弃。
|
||||
@@ -1113,6 +1126,7 @@ fn build(
|
||||
Err(_) => error_response(StatusCode(500), "expression execution failed internally"),
|
||||
};
|
||||
}
|
||||
// 一次性构建开始前先拒绝过期 job,节省无权发布的执行;锁外求值结束后仍须再次核验,不能只检查一次。
|
||||
match namespace.read() {
|
||||
Ok(current) if current.submission != job.submission => return superseded(job.submission),
|
||||
Err(_) => return error_response(StatusCode(500), "namespace lock is poisoned"),
|
||||
@@ -1127,6 +1141,7 @@ fn build(
|
||||
let Ok(mut current) = namespace.write() else {
|
||||
return error_response(StatusCode(500), "namespace lock is poisoned");
|
||||
};
|
||||
// 这是计算后的第二次检查;期间新提交即使源码相同也拥有新 submission,旧候选不得覆盖它。
|
||||
if current.submission != job.submission {
|
||||
return superseded(job.submission);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user