feat: park viewport refreshes across worker turns and serialize new input
This commit is contained in:
@@ -509,3 +509,6 @@ UiBridge::begin_layout_frame/resume_layout_frame now expose PendingLayout around
|
||||
|
||||
|
||||
ReplSession now owns a resumable RefreshWork containing window traversal, pending bridge layout, unpublished per-window documents and the cumulative refresh work budget. poll_refresh advances one refresh stage or VM quantum; only a complete stable pass replaces the session's full document map. Synchronous callers drain the same state machine. Failure stops the domain, releases any pending bridge work and preserves the externally published picture. refreshYields counts cooperative refresh returns, not wall-clock time. Prepared-Mount live rollback remains unfinished.
|
||||
|
||||
|
||||
Viewport refresh now advances through ReplSession's saved refresh state on worker idle turns. A turn advances one traversal stage or a 256-instruction layout quantum; a pending job shortens the next worker wait to 1 ms. Tasks/window commands and document publication wait for complete refresh. ui.stats().refreshYields reports cooperative idle returns. Mutating requests first drain the existing job, so they cannot run inside its read-only scope. This is serialized input, not live cancellation/rollback or latest-input coalescing. Initial loads, explicit REPL/event refresh and task callbacks still drain synchronously; native Rust measurement/paint calls remain atomic.
|
||||
|
||||
@@ -161,3 +161,6 @@ UiBridge::begin_layout_frame/resume_layout_frame now expose PendingLayout around
|
||||
|
||||
|
||||
ReplSession now owns a resumable RefreshWork containing window traversal, pending bridge layout, unpublished per-window documents and the cumulative refresh work budget. poll_refresh advances one refresh stage or VM quantum; only a complete stable pass replaces the session's full document map. Synchronous callers drain the same state machine. Failure stops the domain, releases any pending bridge work and preserves the externally published picture. refreshYields counts cooperative refresh returns, not wall-clock time. Prepared-Mount live rollback remains unfinished.
|
||||
|
||||
|
||||
Viewport refresh now advances through ReplSession's saved refresh state on worker idle turns. A turn advances one traversal stage or a 256-instruction layout quantum; a pending job shortens the next worker wait to 1 ms. Tasks/window commands and document publication wait for complete refresh. ui.stats().refreshYields reports cooperative idle returns. Mutating requests first drain the existing job, so they cannot run inside its read-only scope. This is serialized input, not live cancellation/rollback or latest-input coalescing. Initial loads, explicit REPL/event refresh and task callbacks still drain synchronously; native Rust measurement/paint calls remain atomic.
|
||||
|
||||
+34
-3
@@ -94,7 +94,16 @@ fn start_server_observed(
|
||||
let mut worker = Worker::default();
|
||||
loop {
|
||||
worker.refresh_viewport(&worker_namespace, &worker_notify);
|
||||
match receiver.recv_timeout(std::time::Duration::from_millis(100)) {
|
||||
let wait = if worker
|
||||
.repl
|
||||
.as_ref()
|
||||
.is_some_and(|(_, runtime)| runtime.has_pending_refresh())
|
||||
{
|
||||
std::time::Duration::from_millis(1)
|
||||
} else {
|
||||
std::time::Duration::from_millis(100)
|
||||
};
|
||||
match receiver.recv_timeout(wait) {
|
||||
Ok(job) => {
|
||||
let response = worker.run(&job, &worker_namespace, &worker_notify);
|
||||
let _ = job.request.respond(response);
|
||||
@@ -503,6 +512,15 @@ impl Worker {
|
||||
}
|
||||
|
||||
fn refresh_viewport(&mut self, namespace: &Arc<RwLock<NamespaceState>>, notify: &Notify) {
|
||||
self.refresh_viewport_mode(namespace, notify, false);
|
||||
}
|
||||
|
||||
fn refresh_viewport_mode(
|
||||
&mut self,
|
||||
namespace: &Arc<RwLock<NamespaceState>>,
|
||||
notify: &Notify,
|
||||
drain: bool,
|
||||
) {
|
||||
let (epoch, viewports) = match namespace.read() {
|
||||
Ok(current) => {
|
||||
let mut viewports: std::collections::BTreeMap<_, _> = current
|
||||
@@ -537,7 +555,14 @@ impl Worker {
|
||||
runtime.poll_tasks()?;
|
||||
return Ok((None, Vec::new()));
|
||||
}
|
||||
let resized = runtime.resize_windows(&viewports)?;
|
||||
let resized = if drain {
|
||||
runtime.resize_windows(&viewports)?
|
||||
} else {
|
||||
runtime.resize_windows_incremental(&viewports, false)?
|
||||
};
|
||||
if runtime.has_pending_refresh() {
|
||||
return Ok((None, Vec::new()));
|
||||
}
|
||||
let completed = runtime.poll_tasks()?;
|
||||
Ok((completed.or(resized), runtime.take_window_commands()?))
|
||||
}));
|
||||
@@ -604,7 +629,10 @@ impl Worker {
|
||||
if job.kind == RequestKind::Expression {
|
||||
return build(job, namespace, notify);
|
||||
}
|
||||
self.refresh_viewport(namespace, notify);
|
||||
// Mutating requests serialize behind a pending refresh. Idle polling
|
||||
// parks between quanta, but input never interleaves heap mutation with
|
||||
// an active read-only call or publishes a partly refreshed window set.
|
||||
self.refresh_viewport_mode(namespace, notify, true);
|
||||
let epoch = match namespace.read() {
|
||||
Ok(current) => current.submission,
|
||||
Err(_) => return error_response(StatusCode(500), "namespace lock is poisoned"),
|
||||
@@ -1136,6 +1164,9 @@ source_layout::start now returns either Complete(Draft) or Pending(Work). The ho
|
||||
|
||||
UiBridge::begin_layout_frame/resume_layout_frame now expose PendingLayout around the source job. This bridge-level continuation also owns its target window, page snapshot, tab projection, viewport restoration and retained lifecycle hook root. Only the Complete branch projects scroll/focus, captures dependency versions, merges derived caches, commits Mount geometry and runs deferred hooks. Resume restores the saved window context, and failure releases hook roots. layout_frame remains a synchronous adapter over this protocol. Cross-worker-turn scheduling and cancellation/rollback of prepared mounts are still unfinished; callers must not drop a pending bridge layout as a substitute for cancellation.
|
||||
|
||||
|
||||
Viewport refresh now advances through ReplSession's saved refresh state on worker idle turns. A turn advances one traversal stage or a 256-instruction layout quantum; a pending job shortens the next worker wait to 1 ms. Tasks/window commands and document publication wait for complete refresh. ui.stats().refreshYields reports cooperative idle returns. Mutating requests first drain the existing job, so they cannot run inside its read-only scope. This is serialized input, not live cancellation/rollback or latest-input coalescing. Initial loads, explicit REPL/event refresh and task callbacks still drain synchronously; native Rust measurement/paint calls remain atomic.
|
||||
|
||||
## UI host object
|
||||
|
||||
Positional arguments only:
|
||||
|
||||
Reference in New Issue
Block a user