cron: hold job state in Cell/JsCell, take ThisPtr instead of *mut Self

Merged
JA
3mo ago
+453-607

What

Same treatment as #40002 for the three job types in src/runtime/api/cron.rs: CronRegisterJob / CronRemoveJob (the Bun.cron.register/remove subprocess state machines) and CronJob (in-process Bun.cron() timers).

  • CronRegisterJob/CronRemoveJob: state, process, stdout_reader/stderr_reader, remaining_fds, has_called_process_exit, exit_status, err_msg, tmp_path move into Cell/JsCell. Every method on the path to finish() (maybe_finished, advance_state, on_reader_done/error, on_process_exit, start_*, spawn_*, spawn_cmd_generic/prepare) takes ThisPtr<Self> and returns without touching this after the call that may free it; the prepare_* helpers take &self. finish holds the single heap::take. The pipe-reader and process-exit callbacks (macro-provided unsafe bodies) build the ThisPtr at entry.
  • CronJob was already Cell/JsCell; its helpers (release_pending_ref, finish_deferred_stop, self_stop, remove_from_list, schedule_next, on_timer_fire) move from *mut Self + a private from_ctx_ptr deref to ThisPtr<Self>; the hand-rolled as_ctx_ptr/ref_guard/CronJobDerefOnDrop are replaced by bun_ptr::AsCtxPtr, ThisPtr::ref_guard and destroy_box_with. dispatch.rs builds the ThisPtr in the timer-fire arm.
  • The s!() macro in spawn_cmd_prepare that reborrowed the whole job as &mut across reader.start() (which can re-enter on_reader_error and write sibling fields) is gone; the reader is driven through its own cell and the re-entrant writes land on Cells.

unsafe count: cron.rs 124 → 34 (remaining: raw Process intrusive-refcount calls, the two heap::take frees, ThisPtr::new at the entry points, CronJob::deref, the jsc_host_abi! shims, spawn_process FFI).

Testing

Debug+ASAN build with cronie installed so the crontab paths actually run: cron.test.ts 82 pass / 35 skip (launchd/schtasks blocks), in-process-cron 27, cron-local-time 30, cron-parse 22 pass / 1 pre-existing debug-timing failure ("Feb 30 returns null quickly" asserts <50 ms; ~200 ms on any debug build, identical on main). Manual: PATH=/nonexistent register/remove reject synchronously with "crontab not found in PATH"; full register → crontab -l shows entry → remove → gone. bun scripts/rust-check-all.ts x86_64-pc-windows-msvc aarch64-apple-darwin: 2 ok (mac/windows arms type-check).