Skip to content

FEATURE: add Context#call_async and Context#eval_async - #441

Open
davidtaylorhq wants to merge 1 commit into
fix-roundtrip-request-dropfrom
async-support
Open

FEATURE: add Context#call_async and Context#eval_async#441
davidtaylorhq wants to merge 1 commit into
fix-roundtrip-request-dropfrom
async-support

Conversation

@davidtaylorhq

Copy link
Copy Markdown
Collaborator

These work like call and eval, except that when the result is a promise they block until it settles and return the settled value. A rejected promise raises MiniRacer::RuntimeError, like a synchronous throw, and non-promise results are returned as-is.

While waiting, the V8 thread alternates between draining the microtask queue and pumping the platform message loop in wait-for-work mode, so there is no polling: microtask chains settle immediately, and delayed or background work (Atomics.waitAsync timers, async wasm compilation) wakes the loop when its tasks are posted.

TerminateExecution doesn't wake a parked message loop, and when called while no JS is running it only queues a termination for the next JS entry. v8_terminate_execution therefore also sets a flag on the State and posts a no-op wakeup task, so the timeout watchdog, Context#stop and Ruby thread interrupts can all end a pending await. A promise that can never settle blocks like an infinite loop until one of those stops it.

The new methods use two new request opcodes ('D' and 'F') sharing the existing v8_call/v8_eval implementations. Ruby callbacks invoked while waiting go through the usual nested-dispatch path, and exceptions they raise propagate out through the promise rejection. TruffleRuby raises MiniRacer::Error.

These work like call and eval, except that when the result is a promise they block until it settles and return the settled value. A rejected promise raises MiniRacer::RuntimeError, like a synchronous throw, and non-promise results are returned as-is.

While waiting, the V8 thread alternates between draining the microtask queue and pumping the platform message loop in wait-for-work mode, so there is no polling: microtask chains settle immediately, and delayed or background work (Atomics.waitAsync timers, async wasm compilation) wakes the loop when its tasks are posted.

TerminateExecution doesn't wake a parked message loop, and when called while no JS is running it only queues a termination for the next JS entry. v8_terminate_execution therefore also sets a flag on the State and posts a no-op wakeup task, so the timeout watchdog, Context#stop and Ruby thread interrupts can all end a pending await. A promise that can never settle blocks like an infinite loop until one of those stops it.

The new methods use two new request opcodes ('D' and 'F') sharing the existing v8_call/v8_eval implementations. Ruby callbacks invoked while waiting go through the usual nested-dispatch path, and exceptions they raise propagate out through the promise rejection. TruffleRuby raises MiniRacer::Error.
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