stdlib: split the promise bridge from the tokio runtime (turnloop P8 lane L) - #11115
proggeramlug wants to merge 2 commits into
Conversation
…lane L) perry-stdlib's `async-runtime` feature was both the promise bridge (the settle queue and main-thread pump every native binding resolves through) and tokio's current-thread runtime, and the auto-optimize driver forced it onto every stdlib-linking program. The bridge is now `async-bridge`, with no tokio; the runtime moved to `common/tokio_bridge.rs` under `async-runtime = ["async-bridge", "dep:tokio"]`, re-exported through `async_bridge` so no caller's path changed. crypto, bcrypt, argon2, zlib (bundled and perry-ext-zlib's pump), nodemailer's bundled copy, readline, worker_threads and UI builds now need only the bridge. The driver forces `async-bridge` and selects `async-runtime` for every shared-tokio wrapper via `binding_needs_shared_tokio`. Without tokio, `perry_ffi_spawn_blocking` runs on turnloop's Occupancy::Long set (PerryTS/turnloop#42) through a new `turnloop_pool::submit_long`, and `perry_ffi_spawn_async` / `_with_reactor` are not compiled.
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (18)
Included review availability: Your plan provides up to 8 included reviews per hour; 3 remain after this review. 📝 WalkthroughWalkthroughThe stdlib async bridge now compiles without Tokio, while Tokio runtime operations remain behind ChangesAsync Bridge and Blocking Work
Priority: ➖ Normal Estimated code review effort: 4 (Complex) | ~45 minutes Change: Refactor Sequence Diagram(s)sequenceDiagram
participant perry_ffi_async
participant turnloop_pool
participant LongWorker
participant FallbackThread
perry_ffi_async->>turnloop_pool: submit_long(work, deliver)
turnloop_pool->>LongWorker: schedule work with Long occupancy
perry_ffi_async->>FallbackThread: run work if submission is refused
Merge Risk: ⚪ Minimal · up to The promise bridge now builds without Tokio. Crypto, compression, readline, worker, and UI programs link smaller stdlib archives, while network and database features still pull in the Tokio runtime. No concrete breakage was identified, and the change looks ready to merge. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…entory Two conflicts, both from #10704 (decimal removal) and #11115 (lane L) touching the same files: crates/perry/src/commands/stdlib_features.rs -- #10704 deletes the decimal.js/bignumber.js arm; #11115 rewords the readline comment from 'async-runtime feature' to 'promise bridge'. Kept BOTH: the deletion and the rewording. scripts/tokio_inventory.json -- lane L's post-split description supersedes the pre-split text, so take theirs wholesale rather than merging. One correction on top: their 'In order:' list still has lane A as future work, but #11105 landed in train 266. tokio inventory: 13 edges across 6 crates, 14 packages
Part of the turnloop P8 tokio removal, lane L (
scripts/tokio_inventory.jsongroup L:perry-stdlib -> tokio).What this does
perry-stdlib's
async-runtimefeature was two things welded together: the promise bridge (the settle queue and main-thread pump that every native binding, stdlib orperry-ext-*, resolves its promises through) and tokio's current-thread runtime. The auto-optimize driver force-enabledasync-runtimefor every stdlib-linking program, so every such binary carried tokio, including programs that never hand tokio a future.This PR splits them:
common/async_bridge.rs: pinning,InflightGuard, both settle queues, GC scanner,ensure_pump_registered,js_stdlib_process_pending,js_stdlib_has_active_handles,pool_for_promise_deferred, newreject_promise_laterasync-bridge(new)common/tokio_bridge.rs(new file, moved verbatim):RUNTIME, wait-driver tick,spawn,spawn_for_promise*,block_on,run_one_tick,drive_pendingasync-runtime=async-bridge+dep:tokiotokio_bridge's public names are re-exported throughasync_bridge, so no caller's path changed.ws.rs,tls.rs,net/,fetch/are untouched (sibling lanes G #11101 and H #11102 own them).perry_ffi_async.rs:perry_ffi_spawn_asyncandperry_ffi_spawn_blocking_with_reactorcompile only underasync-runtime, because their payload is a tokio future. Without the feature they are left out and the link fails naming them. They are not stubbed out to abort at runtime instead.perry_ffi_spawn_blockingkeeps its tokio-blocking-pool arm underasync-runtime. Without it, the closure runs on turnloop'sOccupancy::Longworker set (Pool has no long-occupancy job class: connection-lifetime work cannot move off tokio turnloop#42) through a newperry_runtime::turnloop_pool::submit_long, falling back to one plain thread (same worker_threads: a worker never runs module init (__perry_init_done_*is process-wide), so it aliases the spawning thread's heap — object literals read back property-less (OpenCode TUI wall) #10399 stack size) on a thread with no loop.perry_ffi_run_pendingwithout tokio gives the whole budget to a bounded turn.Features:
crypto,bundled-bcrypt,bundled-argon2,compression-gzip,bundled-nodemailer,external-zlib-pumpnow implyasync-bridgeinstead ofasync-runtime. Every feature that hands tokio a future keepsasync-runtime:web-fetch,bundled-net,tls-runtime,external-tls-server,external-net-tls,bundled-ws,external-{net,ws,http-server,http-client}-pump,container.fullstill implies it, so prebuilt archives andPERRY_NO_AUTO_OPTIMIZEbuilds are unchanged.CLI (
optimized_libs/driver.rs): the unconditional force (and the UI force, andreadline's mapping) becomesasync-bridge.async-runtimeis now selected for every shared-tokio wrapper throughbinding_needs_shared_tokio, the same predicate the #507 rebuild and the #7629 coherence check use, so the three cannot disagree. The existing pg/mysql2 andbundled-ws/bundled-net/http-clientrules are kept.turnloop#42 / alpha.8
PerryTS/turnloop#42 is closed.
Occupancy::Longlanded in turnloop #74 and first shipped in 0.1.0-alpha.5. Perry already pins alpha.6, so this PR does not need #11083's alpha.8 bump and is based on plainorigin/main.submit_longkeeps the existing Bounded path on the literalLoop::blockingcall every P4 job already makes, and only the Long class goes throughblocking_with.Evidence (perrymaster, Node 26.5.1 from
/opt/node-v26.5.1-linux-x64)Both arms were built the same way from one shared target dir:
cargo build --release -p perry -p perry-runtime-static -p perry-stdlib-staticwithCARGO_PROFILE_RELEASE_CODEGEN_UNITS=16. Base isd8f24f15e(origin/main at branch point); the branch was later rebased onto9d2693629with no conflicts, then re-checked withcargo checkand the gates below.The subject: tokio leaves auto-optimized programs. Three fixtures (crypto async callbacks + WebCrypto,
node:zlibrouted to perry-ext-zlib, timer ordering) were compiled with auto-optimize on and run 3x each:.atokio-1.strings in binaryasync-runtime,cryptoasync-bridge,cryptoasync-runtime,external-zlib-pumpasync-bridge,external-zlib-pumpasync-runtimeasync-bridge(The base timers binary diffed from Node on its first 3 runs while the host was compiling:
t30fired beforet0. It matched 6/6 when rerun on a quiet host, and the branch binary also matched 6/6 then. Neither timers binary links tokio, and both are the same size, so this is a load-timing flake that exists on main.)Shared-tokio programs still get tokio: the branch's auto-optimize archives for the net and ws/http gap tests were built with
async-bridge,async-runtime,external-net-pump,…and each bundles 16 tokio CGUs.Gap A/B: 53 tests, every
test_gap_*matching ws / net_ / socket / crypto / zlib / bcrypt / argon2 / fetch / timer / readline / worker. HarnessPERRY_SKIP_BUILD=1 ./run_parity_tests.sh --filter <name>, one test at a time. The ext-routed ones (ws, net, zlib) are auto-optimized by the harness itself.npm ciin the tree, the 4test_gap_turnloop_ws_*tests are PARITY_FAIL on both arms, because the Node oracle cannot resolvews. The base and branch perry outputs were byte-identical. Afternpm ci --ignore-scriptsboth arms pass.Unit tests (release, CGU16,
RUST_TEST_THREADS=1):perry-runtime turnloop_pool: 11/11, including the newa_long_job_runs_while_every_bounded_worker_is_held. It holds every bounded worker on a gate (read fromturnloop::pool_stats, not assumed) and requires the long job to complete anyway. Sabotage-checked: routingsubmit_longtoOccupancy::Boundedmakes it fail.perry-stdlibbridge tests (full features): 7/7. That covers the newreject_promise_later_rejects_with_the_message_on_the_next_pumpand the three moved tokio tests, now intokio_bridge::tests.perry-stdlib --no-default-features --features crypto,compression,email(tokio-free): newperry_ffi_async::tokio_free_tests::spawn_blocking_without_tokio_runs_off_thread_and_releases_its_inflightpasses; 78 passed, 2 failed.crypto::random::tests::native_dispatch_{pbkdf2,random_bytes}_value_form_fires_callback. They fail identically on base (base: 79 passed, 2 failed, same two), so they are pre-existing under--release.perry-stdlib --librun,streams::pipe::tests::pipe_keeps_locks_until_async_abort_settlesaborts the test binary on both base and branch. Also pre-existing.perry --bin perry -- optimized_libs stdlib_features shared_tokio: 63/63.Checks and gates:
RUSTFLAGS="-D warnings" cargo check -p perry-stdlib -p perry-runtime -p perry -p perry-ffi --all-targets(dev profile): clean. The tokio-free feature combinationsasync-bridge,crypto,compression-gzip,bundled-nodemailer,bundled-argon2,bundled-bcrypt,external-zlib-pumpeachcargo checkclean, andcargo tree -i tokiofinds no tokio in any of them.run_lint_gates.shis broken at its cargo-xwin step):cargo fmt --check,check_file_size.sh,tokio_inventory.py --self-test+ check,gc_runtime_root_holders.py --self-test+ check,unrooted_local_shape.py --checkand--no-raise-vs,raw_handle_debt.py(both),addr_class_inventory.py,gc_pin_sites.py,check_cross_thread_promise_provenance.py,gc_root_dominance_check.py --audit-*,check_gc_scanner_latches.py,global_sink_isolation.py(both),lock_no_downgrade.py,check_test_registration.py,parity_known_failures.py --auditand 14 more: all 36 OK.Inventory
python3 scripts/tokio_inventory.py: 17 edges before, 17 after. Theperry-stdlib -> tokiomanifest edge stays, becausefulland every tokio-socket feature still imply it. What moved is which programs reach it. The edge'ssurface/reached_when/blockerannotations are rewritten to say exactly that.source_sites.perry-stdlibis 73 -> 74 (the new file's header comment).What remains for lane L (the whole edge)
With this split, the edge is deleted by removing
tokio_bridge.rs, the threecfg(feature = "async-runtime")shims inperry_ffi_async.rsanddep:tokio, then foldingasync-runtimeintoasync-bridge. Nothing inasync_bridge.rshas to move. What still selectsasync-runtime, in the order it can go:web-fetch: after deps(fetch): remove perry-stdlib's reqwest fallback (tokio group G) #11101 (lane G) lands, fetch has no tokio call sites.web-fetchthen needs onlyasync-bridge, andhttp-clientcan move to the bridge list in the driver. That is a one-line follow-up, and I deliberately did not stack on deps(fetch): remove perry-stdlib's reqwest fallback (tokio group G) #11101.bundled-net,tls-runtime,external-tls-server,external-net-tls,bundled-ws:net/mod.rs,tls.rsandws.rsstillspawntokio socket tasks. turnloop(P8 H): move perry-stdlib's TLS off tokio-rustls onto a sans-I/O rustls session #11102 (lane H) moves TLS off tokio-rustls but keeps tokio sockets, so P1 (turnloop handles under those three modules) is still needed.external-{net,ws,http-server,http-client}-pump: perry-ext-net / -http hand tokio futures toperry_ffi_spawn_asyncand_with_reactor(lane A). perry-ext-net'supgradeTLSreply wait also callsHandle::current()insideperry_ffi_spawn_blocking.Handle::current().block_oninsideperry_ffi_spawn_blocking, which is only legal on tokio's own blocking pool (group B).container: perry-container-compose is async tokio (group K).shared_tokio.rs,run_pipeline.rs,optimized_libs/driver.rs,no_auto.rs) goes dead and can be deleted. I left it alone here because tokio is not gone.Not run
cargo xwin checkfor Windows. This PR adds nocfg(windows)code.cargo check -p perry-stdlib --no-default-features --features web-fetchfails on main as well (turnloop_clientusesbase64, whichweb-fetchdoes not enable). That is pre-existing, unrelated, and not touched here.scripts/tokio_inventory.json'ssource_sites.perry-stdlibline is edited by this PR, deps(fetch): remove perry-stdlib's reqwest fallback (tokio group G) #11101 and turnloop(P8 H): move perry-stdlib's TLS off tokio-rustls onto a sans-I/O rustls session #11102. Whichever lands second resolves it by rerunningpython3 scripts/tokio_inventory.py --update.Summary by CodeRabbit