From 6728c23ee153cfb6299d00779bbb679b8d853e2d Mon Sep 17 00:00:00 2001 From: Alistair Smith Date: Fri, 12 Jun 2026 11:54:48 -0700 Subject: [PATCH] bake: deref html_bundle in DevServer Drop get_or_put_route_bundle takes an intrusive ref when it creates a RouteBundle, but the raw html_bundle pointer has no Drop and DevServer Drop never released it. One Route per HTML route per dev-server leaked. Zig RouteBundle.deinit:112 does the deref; the Rust port missed it. --- src/runtime/bake/DevServer.rs | 8 ++++++-- src/runtime/bake/dev_server/route_bundle.rs | 11 ++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/runtime/bake/DevServer.rs b/src/runtime/bake/DevServer.rs index f697101b5d75..e03ef0a47a60 100644 --- a/src/runtime/bake/DevServer.rs +++ b/src/runtime/bake/DevServer.rs @@ -1247,6 +1247,10 @@ impl Drop for DevServer { // SAFETY: stored ref from `init_from_any_blob`; no live borrow. unsafe { StaticRoute::deref_(cached.as_ptr()) }; } + // SAFETY: paired with the `ref_` taken in + // `get_or_put_route_bundle` when this bundle was created; the + // raw `html_bundle` field has no Drop, so release it here. + unsafe { bun_ptr::RefCount::::deref(html.html_bundle) }; } } @@ -5376,8 +5380,8 @@ impl DevServer { let file = &mut self.client_graph.bundled_files.values_mut() [incremental_graph_index.get() as usize]; file.html_route_bundle_index = Some(bundle_index); - // Bump the intrusive refcount; matched by - // `RouteBundle::deinit`'s deref of `html_bundle`. + // Bump the intrusive refcount; matched by the + // `html_bundle` deref in `DevServer`'s `Drop`. // SAFETY: `html` is a live IntrusiveRc-managed allocation. unsafe { bun_ptr::RefCount::::ref_(html) }; break 'brk route_bundle::Data::Html(route_bundle::Html { diff --git a/src/runtime/bake/dev_server/route_bundle.rs b/src/runtime/bake/dev_server/route_bundle.rs index 3736176cc045..32a31e21ea2f 100644 --- a/src/runtime/bake/dev_server/route_bundle.rs +++ b/src/runtime/bake/dev_server/route_bundle.rs @@ -164,8 +164,9 @@ impl RouteBundle { } } -// `deinit` is fully subsumed by Drop: -// - client_bundle / cached_response: Option> drop = .deref() -// - Framework: StrongOptional fields drop = .deinit() -// - Html: bundled_html_text Box<[u8]> drop = allocator.free() -// html_bundle RefPtr drop = .deref() +// Zig `RouteBundle.deinit` equivalent, split across two mechanisms: +// - Drop: `Framework` StrongOptional fields (= .deinit()) and +// `Html.bundled_html_text` Box<[u8]> (= allocator.free()). +// - `DevServer`'s `Drop` (explicit): `client_bundle`, `Html.cached_response` +// (BackRef, no Drop) and `Html.html_bundle` (raw ptr, no Drop) each hold +// an intrusive ref that is deref'd there.