Skip to content

Add await_io() and add_object() to the Rust io-context crate - #8030

Draft
bartlomieju wants to merge 2 commits into
biwanczuk/rust-jsg-io-contextfrom
biwanczuk/rust-io-context-await-io
Draft

bartlomieju wants to merge 2 commits into
biwanczuk/rust-jsg-io-contextfrom
biwanczuk/rust-io-context-await-io

Conversation

@bartlomieju

Copy link
Copy Markdown
Collaborator

Stacked on #7963; only the last two commits are new here.

This gives Rust APIs the two IoContext operations that porting
MemoryCache.read() to Rust needs: waiting for I/O and resuming the
request afterwards, and tying a value's lifetime to the request.

IoContext::await_io()

await_io(future, callback) waits for a Rust future as part of the
request, then calls callback with its output in the IoContext,
with a new lock. The future may complete on any thread; the callback
runs on the IoContext's thread. If the request ends first, both are
dropped without the callback running.

It goes through a new IoContext::awaitIoAndRun(), a public variant
of awaitIo() for callers that settle no jsg::Promise. Both share
the I/O half of awaitIoImpl(), split out unchanged as the private
resumeAfterIo(), so the callback runs as awaitIo()'s continuation
does:

  • holding a pending event, and as a task of the IoContext
  • in the critical section, if any, that was current when the wait
    began
  • in the async context frame that was current when the wait began,
    not whichever is current when the future completes
  • recorded for async tracing, as an awaitIo bridge that causes the
    turn running the callback

awaitIoImpl() keeps its JS-settling half and calls
resumeAfterIo() for the rest; its behaviour is unchanged.
ExceptionOr<T> moves to the public section, since
awaitIoAndRun()'s callback receives one.

IoContext::add_object()

add_object(value) gives the IoContext ownership of a value, as
C++ addObject() does, and returns an IoOwned<T>. Its lifetime
rules match IoOwn<T>'s, including deferring the drop to the
IoContext when the handle is dropped while it is not current, but
access differs: the value can only be moved out with take(), which
returns None once the IoContext is gone. The type's documentation
compares the two.

jsg::Error::new_internal_error()

This is now public, for failures that only a bug causes, such as
calling these methods on an IoContext that is no longer current.
Throwing it gives JavaScript a generic
internal error; reference = <id> and logs the message with that ID.

Tests

io-context-test.c++ runs these in requests of a real worker:

  • the callback runs in the caller's async context frame
  • the wait is recorded for async tracing and causes the turn that
    runs the callback
  • a future completing on another thread wakes the request
  • the future and callback are dropped when the request ends first
  • an owned value is dropped with the request unless taken, and when
    its handle is dropped: at once if the request is current,
    otherwise the next time the request runs

A jsg-test test checks that a thrown internal error is redacted.

Rust APIs need it for failures that only a bug causes, such as an
IoContext that is no longer current. Throwing the error gives
JavaScript a generic "internal error; reference = <id>" Error and
logs the message with that ID. A new test checks the redaction.
IoContext::await_io() waits for a Rust future as part of the request,
then runs a callback with its output in the IoContext. It goes
through IoContext::awaitIoAndRun(), a new public variant of awaitIo()
for callers that settle no jsg::Promise. Both now share the I/O half
of awaitIoImpl(), split out as resumeAfterIo(), so the callback runs
like awaitIo()'s does: holding a pending event, in the critical
section and async context frame current when the wait began, and
recorded for async tracing. If the request ends first, the future and
callback are dropped without the callback running.

IoContext::add_object() gives the IoContext ownership of a value, as
C++ addObject() does. It returns IoOwned<T>, from which the value can
only be taken out, rather than used in place as IoOwn<T> is.
@bartlomieju
bartlomieju force-pushed the biwanczuk/rust-io-context-await-io branch from 05d31f8 to 42efd1c Compare October 28, 2026 21:58
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.

1 participant