From 5a1cf9d7022e53ed018944be472d2b7d1e7deb91 Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Fri, 23 Oct 2026 01:50:17 +0800 Subject: [PATCH] feat: slice large intrinsic layout trees between node calls --- README.md | 3 +++ src/README.md | 3 +++ src/source_layout.rs | 8 ++++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7635c67..bd7cd0f 100644 --- a/README.md +++ b/README.md @@ -524,3 +524,6 @@ New viewport targets now coalesce pending refresh work. The runtime cancels only Cancellation checkpoints own explicit VM roots until restored or released. Initial presentations may have no mount dictionary; cancellation restores that absence too. Saved metadata values retain object-valued attributes. A pending refresh can restart while keeping already completed window drafts; no onMount/onUnmount callback is replayed merely because the viewport target changed. Mount-owned tasks are cancelled immediately before an actual onUnmount callback, never during abandoned preparation. + + +Large intrinsic layout trees (more than 64 occurrences) now use the resumable per-occurrence driver too. Native measurement caches remain active, and a single native callback remains atomic; VM instruction boundaries between nodes allow the worker to yield and replace an obsolete viewport. Small intrinsic trees retain the allocation-free native fast path. Structural extraction, driver preparation, settling checks and final paint/projection still run atomically; this is not a wall-clock preemption guarantee. diff --git a/src/README.md b/src/README.md index 5ebbcff..1472cc4 100644 --- a/src/README.md +++ b/src/README.md @@ -179,3 +179,6 @@ New viewport targets now coalesce pending refresh work. The runtime cancels only Cancellation checkpoints own explicit VM roots until restored or released. Initial presentations may have no mount dictionary; cancellation restores that absence too. Saved metadata values retain object-valued attributes. A pending refresh can restart while keeping already completed window drafts; no onMount/onUnmount callback is replayed merely because the viewport target changed. Mount-owned tasks are cancelled immediately before an actual onUnmount callback, never during abandoned preparation. + + +source_layout uses the same resumable node driver for trees over 64 occurrences even when measure/arrange are intrinsic. Small intrinsic trees retain LayoutRuntime::frame. The larger path keeps LayoutRuntime measurement caches and yields between node calls; structural extraction, bytecode preparation, a single node, settling and paint remain atomic. diff --git a/src/source_layout.rs b/src/source_layout.rs index c8d8665..c56b7cd 100644 --- a/src/source_layout.rs +++ b/src/source_layout.rs @@ -18,6 +18,10 @@ use std::{ sync::{Arc, Mutex}, }; +// Small native trees keep the allocation-free fast path. Larger trees use the +// same per-occurrence VM driver so even intrinsic work can yield between nodes. +const NATIVE_FAST_PATH_MAX_NODES: usize = 64; + fn invalid(message: &'static str) -> VmError { VmError::InvalidArgument(message) } @@ -769,7 +773,7 @@ pub fn start( let live: HashSet<_> = mounts.iter().copied().collect(); cache.entries.retain(|mount, _| live.contains(mount)); // Preserve the existing native measurement cache and avoid VM/result - // allocation for trees without custom layout methods. Contexts already exist. + // allocation for small trees without custom layout methods. Contexts already exist. let mut custom = false; for weak in &mounts { let mount = vm @@ -803,7 +807,7 @@ pub fn start( } // Flex allocation can change text wrapping and ancestor measurements even // when every protocol method is native. Use the same settling driver. - if !custom { + if !custom && nodes.len() <= NATIVE_FAST_PATH_MAX_NODES { cache.clear(); let mut native = runtime.frame(&root)?; // Only the structural pass consumes a published version.