Deduplicate cron job state machines and DNS pending-cache drains - #37363
Deduplicate cron job state machines and DNS pending-cache drains#37363robobun wants to merge 11 commits into
Conversation
…urcemap, threading, url
Resolved src/bun_core/string/mod.rs: #33909 switched return types to crate::CrateResult<()> in write_pre_quoted_string and quote_for_json, which this branch restructures into the const-generic write_pre_quoted_string_inner; carried CrateResult<()> into both the adapter and _inner signatures. Resolved src/router/lib.rs and src/sourcemap/Chunk.rs: #33909 touched return types inside blocks this branch deletes (orphaned router test scaffolding, uncalled print_source_map_contents); kept the deletions after re-verifying zero callers on current main. Restored Node::find_last in src/collections/pool.rs: gained a test caller via #33311.
…update --interactive
There was a problem hiding this comment.
I reviewed this PR and the bug-hunting pass found no issues. Because it restructures a large amount of memory-safety-critical unsafe Rust — raw-pointer receivers with Stacked Borrows reborrow discipline in cron, intrusive-list walks with GC ensure_still_alive bracketing and heap::take ordering in DNS — across three platform cfg arms, a human pass would still be worthwhile.
What was reviewed:
- cron:
base()/base_mut()reborrows still end before everyfinish/maybe_finishedcall;Drop for CronJobCommoncovers the process detach + tmp-file unlink the per-jobDropimpls used to. - cron:
accepts_nonzero_exitpreserves the per-job asymmetry (remove tolerates WindowsInstallingCrontabnonzero, register does not);spawn_bootoutreplacingCronRemoveJob::start_macsets the same state. - dns:
drain_chain_ok/drain_chain_errsnapshotnextbefore consuming each node and keep the originalensure_still_alivebracketing and per-global re-materialization;consume_headruns at the same pointheap::take(key.lookup)did. - dns:
PendingCacheKey<GetAddrInfoRequest>usesinit_query(port+options+name hash) — theNameKeyedbound on plaininitexcludes it, so addr-info can't accidentally hash name-only.
Extended reasoning...
Overview
This PR deduplicates two independent subsystems as split 6/8 of a larger refactor:
src/runtime/api/cron.rs(~500 lines net):CronRegisterJobandCronRemoveJobhad near-identical state machines, process-spawn plumbing, stdout/stderr readers, exit handling, and promise settlement. Shared fields move intoCronJobCommon; the shared logic (maybe_finished,finish,spawn_cmd,start_linux,install_crontab,spawn_bootout) moves onto theCronJobBasetrait as default methods overbase()/base_mut()accessors. Per-job differences reduce toadvance_state,accepts_nonzero_exit, and a Windowsexit_err_overridehook. The separateSpawnCmdTargettrait and its two impls are absorbed intoCronJobBase.src/runtime/dns_jsc/dns.rs(~300 lines net): four copy-pastedPendingCacheKeystructs collapse into one genericPendingCacheKey<Req: HasTail>; fivedrain_pending_*functions collapse their intrusive-list walk into two helpersdrain_chain_err/drain_chain_ok;get_or_put_into_pending_cacheandget_or_put_into_resolve_pending_cachemerge into one generic function.src/runtime/dns_jsc/mod.rs: drops re-exports of the removed per-typePendingCacheKeymodules.
Security risks
None identified. This is internal refactoring with no new user-facing surface, input parsing, or trust-boundary changes. The cron code already single-quotes/XML-escapes user-supplied paths and titles; those call sites are moved, not altered.
Level of scrutiny
High. The change is behavior-preserving in intent, but the code being moved is delicate:
- Cron uses raw
*mut Selfreceivers becausefinish()freesthismid-call-stack; every path relies on local reborrows ending (under NLL) before the freeing call. Moving field access froms.fieldtos.base_mut().field/unsafe { &mut *this }.base_mut()changes the borrow shape and needs re-verification per site. - DNS drain helpers walk intrusive singly-linked lists whose head is embedded in a heap request freed mid-walk, while holding a JSC
JSValuealive across callbacks viaensure_still_aliveand re-materializing it perJSGlobalObject. Getting thenext-snapshot /heap::take/ensure_still_aliveordering wrong is a UAF or a GC-collected value. - Three-way platform
cfggating (linux / macOS / windows) means a mistake can compile on two targets and break the third.
Other factors
- No bugs surfaced from the automated bug-hunting pass.
- I spot-checked several behavior-preservation questions: the merged
CronJobStateenum drops the per-variantcfggates thatRegisterStatehad, which is harmless (unused variants);Drop for CronJobCommonsubsumes both removed per-jobDropimpls (extraCronRegisterJobfields likeabs_path/scheduledrop via fieldDrop);take_filtered_crontabchanges the remove-path OOM message from "Out of memory" to "Out of memory building crontab" — cosmetic. - The PR adds no tests, relying on existing cron (155 tests) and DNS suites; the description notes DNS suites need network and were verified on CI lanes.
- This is part of a stacked series that a maintainer (alii) asked to be split from #32023; the union is stated to be byte-identical to the already-CI'd parent head.
Given the volume of unsafe code being restructured across platform arms, this exceeds the "simple, mechanical, or obvious" bar for auto-approval even with a clean automated pass. Deferring to a human reviewer.
2f5dd36 to
9ae3ff5
Compare
|
Updated 5:05 AM PT - Aug 11th, 2026
❌ @alii, your commit c5e7fe3 has some failures in 🧪 To try this PR locally: bunx bun-pr 37363That installs a local version of the PR into your bun-37363 --bun |
What this does
cron:
CronRegisterJobandCronRemoveJobwere ~600-line near-copies (same process spawn, stdout/stderr readers, exit handling, promise settlement). The shared state moves intoCronJobCommon, and the two jobs implement a small trait supplying the per-job differences: the state sequence andaccepts_nonzero_exit(remove toleratescrontab -lexit 1 on an empty crontab, and on Windows a missing task). The raw-pointer receiver discipline (this: *mut Self, local reborrows ended before any call that may free the job) is unchanged. dns: fourPendingCacheKeytypes and fivedrain_pending_*functions collapse into one generic keyed by aNameKeyed/HasTailtrait pair and two drain helpers (drain_chain_ok/drain_chain_err) that keep the originalensure_still_alivebracketing and per-global re-materialization.Split 6 of 8 from #32023, which alii asked to be broken up. Pure code motion and deletion; no intended behavior change. Stacked on
claude/split/install-cli(merge order: foundations → install-cli → these, in any order among themselves).Notes from rebasing against main
Main renamed
bun_core::immutable::trimtobun_core::strings::trim(#33035); applied in the sharedmaybe_finished.Verification
cargo check), and the union of all eight splits is byte-identical to Deduplicate jsc bindings, runtime api, test matchers, and shell builtins #32023's headf29ce8fe4, which CI ran at 285/286 green (the one red was the fleet-widenapi.test.tsGC-timing flake on windows x64-baseline).