From ae89dfad1eb99b077fcfda95ffd14e9a13935ce8 Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Fri, 23 Oct 2026 11:24:05 +0800 Subject: [PATCH] test: bound managed preparation overhead across large occurrence counts --- README.md | 5 +++- src/README.md | 5 +++- src/api.rs | 4 +++- src/endor_host.rs | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 140351e..8050969 100644 --- a/README.md +++ b/README.md @@ -544,4 +544,7 @@ Complete-source candidates now separate one-time compilation/source execution fr The API keeps the old runtime alongside a pending PUT candidate. Newer submissions cancel only the obsolete candidate; failures restore the published runtime's submission owner. Background epoch checks distinguish accepted replacements from DELETE revocation, so queued/superseded candidates do not destroy the previous live heap. Candidate resumption reads only the primary shell viewport, avoiding reuse of an old secondary window's dimensions for a new VM's resource with the same numeric ID. Success publishes the complete candidate document and commands together, then replaces the old runtime. -Source paint now uses a resumable read-only VM call with the same 256-instruction quantum and a cumulative 1,000,000-instruction budget. The pending job owns the unpublished frame, visual snapshot, decoded command buffer and prepare root. The refresh worker resumes paint before advancing to another window and publishes only the full stable window set. New viewport targets cancel unfinished paint, discard partial commands and force that window to refresh. Already completed layout/Mount geometry and lifecycle callbacks remain committed; cancelling paint does not replay or undo them. Failure/discard explicitly closes the read-only continuation and releases its roots. paintSlices reports the most recently completed paint solve (1 when no override needs a pause); paintCancels counts viewport replacements that closed pending paint. Layout checkpoint rollback remains limited to unfinished layout preparation. Native callback bodies, driver preparation, command decoding/projection and rasterization remain atomic. +Source paint now uses a resumable read-only VM call with the same 256-instruction quantum and a cumulative 1,000,000-instruction budget. The pending job owns the unpublished frame, visual snapshot, decoded command buffer and prepare root. The refresh worker resumes paint before advancing to another window and publishes only the full stable window set. New viewport targets cancel unfinished paint, discard partial commands and force that window to refresh. Already completed layout/Mount geometry and lifecycle callbacks remain committed; cancelling paint does not replay or undo them. Failure/discard explicitly closes the read-only continuation and releases its roots. paintSlices reports the most recently completed paint solve (1 when the solve completes within its first quantum); paintCancels counts viewport replacements that closed pending paint. Layout checkpoint rollback remains limited to unfinished layout preparation. Native callback bodies, driver preparation, command decoding/projection and rasterization remain atomic. + + +Layout and paint drivers now prepare one occurrence at a time. Their fixed-size outer bytecode repeats on the same VM frame; a short-lived helper retains only the current call, its bounds/arguments and collector. The helper is released before the next occurrence. Layout keeps bottom-up measure, top-down arrange and the same settling loop. Paint advances even intrinsic method classification through VM quanta. The weak occurrence list and native scratch/output data remain proportional to the actual tree, but preparation no longer allocates managed callables/Bounds for the whole tree before the first slice. All helpers share the original read-only scope and cumulative instruction/frame limits. Weak-list copying, structural extraction, settling and final projection remain bounded atomic native work. diff --git a/src/README.md b/src/README.md index 02deb9f..95cdc41 100644 --- a/src/README.md +++ b/src/README.md @@ -199,4 +199,7 @@ Complete-source candidates now separate one-time compilation/source execution fr The API keeps the old runtime alongside a pending PUT candidate. Newer submissions cancel only the obsolete candidate; failures restore the published runtime's submission owner. Background epoch checks distinguish accepted replacements from DELETE revocation, so queued/superseded candidates do not destroy the previous live heap. Candidate resumption reads only the primary shell viewport, avoiding reuse of an old secondary window's dimensions for a new VM's resource with the same numeric ID. Success publishes the complete candidate document and commands together, then replaces the old runtime. -Source paint now uses a resumable read-only VM call with the same 256-instruction quantum and a cumulative 1,000,000-instruction budget. The pending job owns the unpublished frame, visual snapshot, decoded command buffer and prepare root. The refresh worker resumes paint before advancing to another window and publishes only the full stable window set. New viewport targets cancel unfinished paint, discard partial commands and force that window to refresh. Already completed layout/Mount geometry and lifecycle callbacks remain committed; cancelling paint does not replay or undo them. Failure/discard explicitly closes the read-only continuation and releases its roots. paintSlices reports the most recently completed paint solve (1 when no override needs a pause); paintCancels counts viewport replacements that closed pending paint. Layout checkpoint rollback remains limited to unfinished layout preparation. Native callback bodies, driver preparation, command decoding/projection and rasterization remain atomic. +Source paint now uses a resumable read-only VM call with the same 256-instruction quantum and a cumulative 1,000,000-instruction budget. The pending job owns the unpublished frame, visual snapshot, decoded command buffer and prepare root. The refresh worker resumes paint before advancing to another window and publishes only the full stable window set. New viewport targets cancel unfinished paint, discard partial commands and force that window to refresh. Already completed layout/Mount geometry and lifecycle callbacks remain committed; cancelling paint does not replay or undo them. Failure/discard explicitly closes the read-only continuation and releases its roots. paintSlices reports the most recently completed paint solve (1 when the solve completes within its first quantum); paintCancels counts viewport replacements that closed pending paint. Layout checkpoint rollback remains limited to unfinished layout preparation. Native callback bodies, driver preparation, command decoding/projection and rasterization remain atomic. + + +Layout and paint drivers now prepare one occurrence at a time. Their fixed-size outer bytecode repeats on the same VM frame; a short-lived helper retains only the current call, its bounds/arguments and collector. The helper is released before the next occurrence. Layout keeps bottom-up measure, top-down arrange and the same settling loop. Paint advances even intrinsic method classification through VM quanta. The weak occurrence list and native scratch/output data remain proportional to the actual tree, but preparation no longer allocates managed callables/Bounds for the whole tree before the first slice. All helpers share the original read-only scope and cumulative instruction/frame limits. Weak-list copying, structural extraction, settling and final projection remain bounded atomic native work. diff --git a/src/api.rs b/src/api.rs index e00b5ba..5efb1a6 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1501,7 +1501,9 @@ REPL/event requests retain their HTTP request and scalar result until the comple Complete-source PUT also parks an isolated candidate VM during initial layout. The worker retains its HTTP request and the prior live VM, advances one candidate stage/quantum per turn and commits only after its full window set settles. New PUT/DELETE submissions discard obsolete candidates and explicitly cancel their pending layout. Failure keeps the prior live owner; accepting a replacement alone does not revoke that owner. Candidate viewport updates use only the surviving primary shell; secondary native resource IDs and sizes still belong to the old VM until successful publication. Parsing, compilation, source execution, structural preparation and final command projection remain atomic; source paint execution can yield. -Source paint now uses a resumable read-only VM call with the same 256-instruction quantum and a cumulative 1,000,000-instruction budget. The pending job owns the unpublished frame, visual snapshot, decoded command buffer and prepare root. The refresh worker resumes paint before advancing to another window and publishes only the full stable window set. New viewport targets cancel unfinished paint, discard partial commands and force that window to refresh. Already completed layout/Mount geometry and lifecycle callbacks remain committed; cancelling paint does not replay or undo them. Failure/discard explicitly closes the read-only continuation and releases its roots. paintSlices reports the most recently completed paint solve (1 when no override needs a pause); paintCancels counts viewport replacements that closed pending paint. Layout checkpoint rollback remains limited to unfinished layout preparation. Native callback bodies, driver preparation, command decoding/projection and rasterization remain atomic. +Source paint now uses a resumable read-only VM call with the same 256-instruction quantum and a cumulative 1,000,000-instruction budget. The pending job owns the unpublished frame, visual snapshot, decoded command buffer and prepare root. The refresh worker resumes paint before advancing to another window and publishes only the full stable window set. New viewport targets cancel unfinished paint, discard partial commands and force that window to refresh. Already completed layout/Mount geometry and lifecycle callbacks remain committed; cancelling paint does not replay or undo them. Failure/discard explicitly closes the read-only continuation and releases its roots. paintSlices reports the most recently completed paint solve (1 when the solve completes within its first quantum); paintCancels counts viewport replacements that closed pending paint. Layout checkpoint rollback remains limited to unfinished layout preparation. Native callback bodies, driver preparation, command decoding/projection and rasterization remain atomic. + +Layout and paint drivers now prepare one occurrence at a time. Their fixed-size outer bytecode repeats on the same VM frame; a short-lived helper retains only the current call, its bounds/arguments and collector. The helper is released before the next occurrence. Layout keeps bottom-up measure, top-down arrange and the same settling loop. Paint advances even intrinsic method classification through VM quanta. The weak occurrence list and native scratch/output data remain proportional to the actual tree, but preparation no longer allocates managed callables/Bounds for the whole tree before the first slice. All helpers share the original read-only scope and cumulative instruction/frame limits. Weak-list copying, structural extraction, settling and final projection remain bounded atomic native work. ## UI host object diff --git a/src/endor_host.rs b/src/endor_host.rs index e01ad73..fed786c 100644 --- a/src/endor_host.rs +++ b/src/endor_host.rs @@ -3221,6 +3221,64 @@ page.render(block) ) } + #[test] + fn layout_and_paint_prepare_keep_only_current_occurrence_managed_helpers() { + for copies in [160, 640] { + let (mut session, initial) = + ReplSession::load(&resumable_paint_source(copies, 0), "app.main", 280, 1).unwrap(); + let before = session.candidate.vm.object_count(); + let progress = session + .bridge + .begin_paint_frame( + &mut session.candidate.vm, + initial.ui_root.clone(), + initial.frame, + ) + .unwrap(); + let crate::source_paint::Progress::Pending(work) = progress else { + panic!("paint must yield"); + }; + // Each quantum may retain its current helper/results, not a Bounds + // and collector for every occurrence before reaching the first one. + assert!(session.candidate.vm.object_count() < before + 256); + session + .bridge + .cancel_paint_frame(&mut session.candidate.vm, crate::window::MAIN_WINDOW, *work) + .unwrap(); + assert!(!session.candidate.vm.has_suspended_call()); + session + .bridge + .set_viewport( + &mut session.candidate.vm, + Some(crate::layout::Size { + width: 900.0, + height: 650.0, + }), + ) + .unwrap(); + let root = session + .bridge + .prepare_frame(&mut session.candidate.vm) + .unwrap() + .unwrap(); + let before = session.candidate.vm.object_count(); + let progress = session + .bridge + .begin_layout_frame(&mut session.candidate.vm, root) + .unwrap(); + let crate::ui_bridge::LayoutProgress::Pending(work) = progress else { + panic!("layout must yield"); + }; + assert!(session.candidate.vm.object_count() < before + 256); + session + .bridge + .cancel_layout_frame(&mut session.candidate.vm, *work) + .unwrap(); + assert!(!session.candidate.vm.has_suspended_call()); + assert!(!session.candidate.vm.object_store().is_read_only()); + } + } + #[test] fn pending_paint_retargets_without_replaying_completed_mount_hooks() { let (mut session, initial) =