Skip to content

kj-rs: rework the async bridge waker/event machinery - #7010

Open
danlapid wants to merge 1 commit into
mainfrom
dlapid/rustIoPrep
Open

kj-rs: rework the async bridge waker/event machinery#7010
danlapid wants to merge 1 commit into
mainfrom
dlapid/rustIoPrep

Conversation

@danlapid

Copy link
Copy Markdown
Collaborator

Groundwork for the tokio-backed Rust I/O backend (kj-rs-tokio / kj-rs-io, landing separately); kj-rs itself stays a pure Promise<->Future bridge.

  • Replace KjWaker with FutureWakerCell: every cloned waker is a same-thread cell (non-atomic kj::Refcounted; the bridge's single-thread axiom) that arms the owning FuturePollEvent directly via Event::armDepthFirst(). The cell's link to the event is weak and structurally invalidated when the event dies, so wakers Rust retains past the future's lifetime (e.g. parked in a channel's AtomicWaker) neutralize into safe no-ops instead of arming a freed event. Waker ownership round-trips through RawWaker data slots via kj::Rc::disown()/reown() -- hence the capnp-cpp pin bump to the current v2 head, which carries those (merged upstream).
  • Replace the LinkedGroup machinery (linked-group.h + test) with an intrusive weak link (RustPromiseAwaiter::link / FuturePollEvent::leaves).
  • Make bridged kj::Promises eager by default: the Rust future is polled to its first suspension point at conversion, so KJ callers no longer need a manual .eagerlyEvaluate(nullptr); RustFuture::lazily() is the escape hatch for the rare cold case.
  • Convert panics escaping a bridged future's poll into kj::Exceptions (a rejected promise) instead of aborting the process, mirroring the sync bridge's catch_unwind path.
  • Add a thread-local armed-waker hook so an integrating kj::EventPort that drives tokio tasks inside its own wait() (the upcoming kj-rs-tokio) can nudge itself out of a blocking park; null/no-op by default.
  • Split the cxx bridge module out of lib.rs into ffi.rs, and quarantine unsafe into named FFI islands: deny(unsafe_code) crate-wide, re-allowed per-module only where the FFI seam genuinely needs it.
  • Depend on @capnp-cpp//src/kj:kj-async-core instead of the :kj-async umbrella, keeping kj-rs (and everything built on it) off the concrete kj OS event loop.
  • New tests: waker neutralization across event death (neutralize-waker-test), FuturePollEvent shared-event semantics (shared-event-test), and expanded future/awaiter coverage.

@danlapid
danlapid requested review from a team as code owners August 14, 2026 16:45
@danlapid
danlapid requested a review from mikea August 14, 2026 16:45
@ask-bonk

ask-bonk Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

APIError: Invalid Anthropic API Key

github run

2 similar comments
@ask-bonk

ask-bonk Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

APIError: Invalid Anthropic API Key

github run

@ask-bonk

ask-bonk Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

APIError: Invalid Anthropic API Key

github run

@ask-bonk

ask-bonk Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

@danlapid Bonk workflow failed. Check the logs for details.

View workflow run · To retry, trigger Bonk again.

@codecov-commenter

codecov-commenter commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 132 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.75%. Comparing base (9c08f8a) to head (731a392).

Files with missing lines Patch % Lines
src/rust/cxx/kj-rs/future.rs 0.00% 48 Missing ⚠️
src/rust/cxx/kj-rs/waker.rs 0.00% 48 Missing ⚠️
src/rust/cxx/kj-rs/ffi.rs 0.00% 19 Missing ⚠️
src/rust/cxx/kj-rs/awaiter.rs 0.00% 14 Missing ⚠️
src/rust/cxx/kj-rs/maybe.rs 0.00% 2 Missing ⚠️
src/rust/cxx/kj-rs/promise.rs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7010      +/-   ##
==========================================
- Coverage   67.79%   67.75%   -0.04%     
==========================================
  Files         468      468              
  Lines      132275   132339      +64     
  Branches    21474    21474              
==========================================
  Hits        89671    89671              
- Misses      29528    29593      +65     
+ Partials    13076    13075       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

// only (see requireCurrentOrTearingDown below), so the exception cost is irrelevant.
const kj::Executor* current = nullptr;
auto maybeException =
kj::runCatchingExceptions([&]() { current = &kj::getCurrentThreadExecutor(); });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

surely we can add utility method for you!

@@ -0,0 +1,144 @@
// Regression test: FutureWakerCell "neutralize-on-drop".

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this file tests anything? outside of this test?

@@ -0,0 +1,321 @@
// Regression test: one shared kj Event as the onReady target of MANY concurrent pending nodes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this too doesn't seem to be kj-rs specific?

Groundwork for the tokio-backed Rust I/O backend (kj-rs-tokio / kj-rs-io,
landing separately); kj-rs itself stays a pure Promise<->Future bridge.

- Replace KjWaker with FutureWakerCell: every cloned waker is a same-thread
  cell (non-atomic kj::Refcounted; the bridge's single-thread axiom) that
  arms the owning FuturePollEvent directly via Event::armDepthFirst(). The
  cell's link to the event is weak and structurally invalidated when the
  event dies, so wakers Rust retains past the future's lifetime (e.g.
  parked in a channel's AtomicWaker) neutralize into safe no-ops instead of
  arming a freed event. Waker ownership round-trips through RawWaker data
  slots via kj::Rc::disown()/reown() -- hence the capnp-cpp pin bump to the
  current v2 head, which carries those (merged upstream).
- Replace the LinkedGroup machinery (linked-group.h + test) with an
  intrusive weak link (RustPromiseAwaiter::link / FuturePollEvent::leaves).
- Make bridged kj::Promise<T>s eager by default: the Rust future is polled
  to its first suspension point at conversion, so KJ callers no longer need
  a manual .eagerlyEvaluate(nullptr); RustFuture::lazily() is the escape
  hatch for the rare cold case.
- Convert panics escaping a bridged future's poll into kj::Exceptions (a
  rejected promise) instead of aborting the process, mirroring the sync
  bridge's catch_unwind path.
- Add a thread-local armed-waker hook so an integrating kj::EventPort that
  drives tokio tasks inside its own wait() (the upcoming kj-rs-tokio) can
  nudge itself out of a blocking park; null/no-op by default.
- Split the cxx bridge module out of lib.rs into ffi.rs, and quarantine
  unsafe into named FFI islands: deny(unsafe_code) crate-wide, re-allowed
  per-module only where the FFI seam genuinely needs it.
- Depend on @capnp-cpp//src/kj:kj-async-core instead of the :kj-async
  umbrella, keeping kj-rs (and everything built on it) off the concrete kj
  OS event loop.
- Qualify bare uint as kj::uint in src/rust/kj/tests/ffi-test.c++: these
  dependency changes make that target newly compile on Windows CI, where no
  global uint exists (POSIX gets one from sys/types.h).
- New tests: waker neutralization across event death (neutralize-waker-test),
  FuturePollEvent shared-event semantics (shared-event-test), and expanded
  future/awaiter coverage.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants